Source: http://c.ficherosvirtuales.com/C_1_Utilidades_C_Ficher.html
Timestamp: 2020-02-28 00:25:44+00:00

Document:
C.1 Utilidades. C Ficheros virtuales.doc
C Apéndices
C.1 Utilidades y programas de servicio auxiliares
C.1.1 El programa de servicio SRRCU Utilidades generales
C.1.1.1 Introducción. La búsqueda lineal y dicotómica
C.1.1.2 La resolución de ecuaciones. Métodos Newton y Cuasi-Newton
C.1.1.3 Implementación
C.1.1.3.1 Un ejemplo numérico de resolución con el algoritmo Müller
C.1.1.3.2 Implementación del algoritmo Müller
C.1.2 El programa de servicio SRRCF. Utilidades para cálculo financiero
C.1.3 El programa de servicio SRRCAL. Utilidades de conversión alfanumérica
C.1.4 SRFECHA Servicios de cálculo de fechas y estampillados
C.1.1 El programa de servicio SRRCU. Utilidades Generales
Dentro de las rutinas desarrolladas en SRRCU, destacan la búsqueda dicotómica, básico para SRRCM, y la inversión de funciones utilizadas en los ejemplos de la calculadora hipotecaria y la calculadora en aritmética extendida.
Cuando se desea localizar un ítem en una lista, se pueden emplear distintos algoritmos en función de la información disponible sobre el conjunto de ítems.
Se han presentado en el capítulo de caché la búsqueda lineal y la dicotómica.
La búsqueda lineal, en que simplemente se va comparando el ítem dado con cada ítem de la lista, es la más sencilla pero también la mas lenta de todas con N/2 comparaciones esperadas.
Si se conoce que la lista de ítems está ordenada, se puede aplicar una búsqueda dicotómica, en que se va dividiendo la masa de ítems de 2 en 2 al comparar con ítem de búsqueda. Las comparaciones esperadas bajan a log (N).
Si bien se presentó en la primera zona del libro un extracto de la codificación de la rutina de búsqueda dicotómica SRRCU_BS utilizada en SRRCM, merece la pena incluir su código completo, pues es la base del CHAIN y por ende del núcleo del sistema:
/* FUNCION....: SRRCU_BS */
/* DESCRIPCION: Localiza en una serie ascendente un item */
/* la serie se enlaza segun una serie indice asociada */
/* (Orientada al uso en SRRCM, versión con índice) */
/* PARAMETROS.: */
/* (I) d: Dato a localizar */
/* iSize: Tamaño del dato */
/* nSd: Dimensión efectiva de la serie de datos */
/* Si: Puntero a serie índice */
/* Sd: Puntero a serie de datos */
/* iComp: Variable auxiliar resultado memcmp:- 0 +, < = > */
/* RETORNO....: */
/* n: Indice encontrado, si iComp=0 localización exacta*/
/* Formato 1..nSd */
/*Si la localización es inexacta, se devuelve el indice más próximo*/
/*inferior. */
/*Si el dato solicitado esta fuera rango inferiormente, devuelve 0 */
long SRRCU_BS(const void *d, int iSize, long nSd, const long *Si, const void *Sd, int *iComp)
long a=0,b=0,ab2=0; // Indices de rango en el algoritmo de búsqueda
char *cSd = (char *) Sd; // Uso de Sd como char* (Para seguimiento en depuración)
char *cd = (char *) d;
// Rango inicial
b = nSd;
// Valor extremo inferior
*iComp = memcmp(cd, cSd + Si[a]*iSize, iSize);
if (*iComp<0) return a;
if (!*iComp) return 1;
// Valor extremo superior
*iComp = memcmp(cd, cSd + (Si[b-1] * iSize), iSize);
if (*iComp>=0) return b;
ab2 = (a + b) >> 1;
// Localización exacta
*iComp = memcmp(cd, cSd + (Si[ab2-1] * iSize), iSize); // Instrucción central
// Localización inexacta
Esta codificación la podemos comparar con la versión que utilizaba el SRRCM inicial en que no se utilizaba un índice y la comparación de claves era directa:
/* FUNCION....: SRRCU_BSEARCH */
/* (Orientada al uso en SRRCM, versión inicial) */
/* iSizep: Tamaño del dato */
long SRRCU_BSEARCH(const void *d, int iSize, long nSd, const void *Sd, int *iComp)
long a=0,b=0,ab2=0;// Indices de rango en el algoritmo de búsqueda
*iComp = memcmp(cd, cSd, iSize);
*iComp = memcmp(cd, cSd + ((b-1) * iSize), iSize);
*iComp = memcmp(cd, cSd + ((ab2-1) * iSize), iSize); // Instrucción central
Pero si la búsqueda corresponde realmente a la resolución de una ecuación
Y = F(X), de forma que se busca X = F (Y) para un Y dado, se dispone de herramientas más avanzadas, más eficientes cuanta mayor
información de la función tengamos disponible.
Si se conoce la formulación de la derivada de F, el mejor método es el de Newton-Raphson en donde la progresión de búsqueda se obtiene como
X = X - -------
i+1 i F’(X )
con una velocidad de convergencia cuadrática.
Si la formulación de F’(X) se desconoce, se presentan dos alternativas
La primera es aproximarla acudiendo a su definición y tomar
^ F(X + €) – F(X)
F’(X) = ------------------ donde € se elige contextualmente, p.e. € = 0.01
La segunda es acudir a una algoritmo cuasi-newton denominados así en cuanto a su velocidad de convergencia.
En problemas financieros, que involucran principalmente funciones logarítmico-exponenciales, da muy buen resultado aproximar localmente F por una parábola, es lo que se conoce como método Müller, y este es el método que se ha seguido en el libro.
Si los algoritmos anteriores encuentran dificultades, se puede acudir a métodos más robustos aunque más lentos; como último recurso siempre se puede resolver dicotómicamente.
Es interesante señalar que la mejor rutina para la ordenación, la búsqueda dicotómica, es por el contrario el peor recurso para resolver una función, al que acudir cuando no se disponga de ninguna información adicional sobre la función.
En cuanto se dispone de información, como por ejemplo la forma o aún mejor de la derivada, se pueden aplicar recursos mucho más rápidos y eficientes para su resolución.
En cuanto a la implementación, las funciones anteriores presentan el mismo aspecto al mundo exterior:
double SRRCU_FINVxxx(double dF, double dA, double dB, double (*F)(double X));
double SRRCU_FINVNEW(double dF, double dA, double dB, double (*F)(double X) double (*FD)(double X));
la primera forma es para las versiones que sólo precisan una entrada para la función F(), y la segunda para la versión Newton-Raphson con entrada explícita de F’().
Aquí aparecen los punteros a procedimientos, que se introdujeron en el primer capítulo.
Recordemos que su definición es
tipo (*F) (tipo parámetro1, tipo parámetro 2, ...)
y se utilizan escribiendo simplemente el nombre de la función a pasar.
Así por ejemplo en la calculadora hipotecaria:
// Calcula la cuota ajustada como aquella que proporciona un cuadro de resto nulo
Aquí se calcula una dCuota que hace que el cuadro se explique con un valor residual EPSILON, para un intervalo de partida entre dCuota1 y dCuota2.
Otro ejemplo se implementa en el cálculo del tanto de interés, que es el que vamos a ver en detalle pues es más fácil de seguir:
// para el cálculo del tipo de interés como incógnita del resto de variables se utiliza un procedimiento de inversión cuasi-newton, el
// método Müller, que es un método de obtención de raíces por aproximación de secantes parabólicas
double di = 0.0; // solución buscada, valor de retorno
// Paso de parámetros globales a la rutina Prani
// Cálculo de valores aproximados extremos
di1 = 1.0/ani - 1.0/((double)N);
di2 = 1.0/ani;
// Resolución por el algoritmo Müller
Para la aplicación de éstos algoritmos se introducen funciones auxiliares que responden al prototipo
double (*F)(double X) pues es el requisito impuesto por las funciones SRRCU_FINVxxx indicadas antes.
Como en el ejemplo la función real que se desea invertir es SRRCH_ValorActualRenta_ani, Prani hace de interfaz
double (*F)(double X) con paso implícito de parámetros definidos como variables globales:
// Prani función auxiliar tipo Y = F(X) para cálculo inverso de i dados ani y n
// n se pasa de forma implícita con la variable global N:
static long N = 0; // Paso de n en Prani
double dResul = 0.0; // Variable retorno (Se explicita para facilitar seguimiento por debug)
C.1.1.3.1 Un ejemplo numérico
Volviendo al ejemplo que se introdujo en la calculadora hipotecaria:
que responde a la formulación financiera
Valor final de la renta postpagable asociada:
Valor actual de la renta postpagable asociada:
Calcula el término periodico de una renta renta postpagable, necesario para obtener el valor final sni = 1
Calcula el término periodico de una renta renta postpagable, necesario para obtener el valor actual ani = 1
Ani = i + Pni ó Ani = 1 / ani -> Ani = 0.094559596622951
Cuota = Capital * Ani -> 945.59596622951 (= 10000 * 0.094559596622951)
Si partimos precisamente de la cuota, el algoritmo resolvería como sigue:
Es n = 12 y ani = 10.575341220917180, se ahorquilla el i = 0.02 mensual buscado con los valores extremos iniciales
dA = 1/ani - 1/n = 0.011226263289618177
dB = 1/ani = 0.094559596622951506
Con los que se construyen los puntos de la
dX1 = 0.011226263289618177 -> dY1 = F(X1) = 11.168361999177733
dX2 =
dA + (dB - dA)/10 = 0.019559596622951508 dY2 = F(X2) = 10.604001000071891
dX3 = 0.094559596622951506 dY3 = F(X3) = 6.999144235696035
En la parábola definida por esos tres puntos se busca el X que diera como valor Y = 10.575341220917180 (el ani solicitado)
Utilizando la rutina de ajuste cuadrático SRRCF_AJCUAD (que se presenta más adelante) se obtiene un valor de X = 0.019996120930261041 que proporcionaría a su vez un valor de Y = F(X) = 10.575593161366255 en la función original
Para el nuevo ciclo, se elige el punto de sustitución más cercano al X en curso contrayendo la zona de búsqueda como se indica en el esquema siguiente:
// y . y .
// . -> ó (como es el caso) . ->
// -x-- --- --x- ---
Se eligen entonces como nuevos puntos de la
dX1 = (dX2 anterior) = 0.019559596622951508 -> dY1 = 10.604001000071891
dX2 = (dX obtenido) = 0.019996120930261041 dY2 = 10.575593161366255
dX3 = (Sin cambios) = 0.094559596622951506 dY3 = 6.9991442356960345
En la parábola definida por estos nuevos tres puntos se busca el X que diera como valor Y = 10.575341220917180 (el ani solicitado)
Se obtiene un valor X = 0.019999998317985601 que toma valor Y = F(X) = 10.575341330159818
Se vuelven a sustituir los puntos según el esquema y se parte ahora de la
3ª Parábola. Aproximación a nivel épsilon.
dX1 = (dX2 anterior) = 0.019996120930261041 -> dY2 = 10.575593161366255
dX2 = (dX obtenido) = 0.019999998317985601 dY2 = 10.575341330159818
Como antes, en la nueva parábola definida por estos tres puntos se busca el X que diera como valor Y = 10.575341220917180(el ani solicitado)
Se obtiene un valor X = 0.019999999999992468 que toma valor Y = F(X) = 10.575341220917666 que ya se aproxima al solicitado a un nivel EPSILON lo que determina la parada de la búsqueda
Una vez presentado un ejemplo numérico de resolución, veamos ahora la codificación propuesta del algoritmo Müller, en donde hay que destacar que tras las verificaciones iniciales y los filtros de parada, el núcleo algorítmico
se centra en la resolución de parábolas que se van aproximando a la solución como en el ejemplo.
La convergencia para las funciones usuales del cálculo financiero es muy rápida, cercana a la de newton. Aunque la codificación es más larga que la de newton, para funciones complejas en que la función derivada no está disponible (que desde luego es siempre la mejor alternativa), y su seudocálculo sea pesado, este método es inmejorable.
Se incluye la codificación principal, donde se invoca la resolución de la ecuación de segundo grado que se aborda en el siguiente epígrafe.
// Función: SRRCU_FINVMU
// Descripción: Obtiene el inverso de una función, el X de Y=F(X) por técnica de regula falsi cuadrática (Müller)
// Algoritmo asegurado para función derivable y monótona en el intervalo solicitado, aunque es aplicable en un espectro más
// amplio en que la seguridad ya no es del 100%
// En el caso improbable de fallo, se invocaría SRRCU_FINVRF regula falsi lineal
// (I) dY: Dato a localizar
// dA: Extremo inferior de intervalo X
// dB: Extremo superior de intervalo X
// pF: Puntero a la F(X) a evaluar
// (K) EPSILON: Epsilón de parada, establecido como constante global
// Retorno: dX: Valor de F(Y) encontrado [Y=F(X)] a nivel EPSILON
double SRRCU_FINVMU(double dF, double dA, double dB, double (*F)(double X))
short int er = 0; //Control de errores
short int i=0; //Contador de do para el control anti-bucle de seguridad
double dY=0.0; //Valor de Y probándose
double dX=0.0; //Valor de X probándose
double dY1=0.0; //Valor de Y=F(X1)
double dY2=0.0; //Valor de Y=F(X2)
double dY3=0.0; //Valor de Y=F(X3)
double dX1=0.0; //Valor de X izdo
double dX2=0.0; //Valor de X centro
double dX3=0.0; //Valor de X dcho
double dif=0.0; //Diferencia a evaluar
if (fabs(dA - dB) <= EPSILON) return dA; // Solución "aproximada" al comienzo!
// Puntos de la 1ª parábola
dX1 = dA;
dX2 = dA + (dB - dA)/10.0;
dX3 = dB;
dY1 = (*F)(dX1);
dY2 = (*F)(dX2);
dY3 = (*F)(dX3);
// Los puntos han de diferir
if (dY1 == dY2)
dX1 = SRRCU_VEXI(dA, dB, F); // Su codificación se expone más adelante
dX2 = (dX1 + dB) / 2.0;
if (dY2 == dY3)
dX3 = SRRCU_VEXD(dA, dB, F); // Su codificación se expone más adelante
dX2 = (dX3 + dA) / 2.0;
// Filtro fuera de rango
if ( (dF < dY1) && (dF < dY2) && (dF < dY3) ) return (dY1 < dY3) ? dA:dB;
if ( (dF > dY1) && (dF > dY2) && (dF > dY3) ) return (dY1 < dY3) ? dB:dA;
// Bucle de localización
for (i=0; i < 200; ++i)
// Prueba secundaria
if ( ((fabs(dY2 - dY3) <= EPSILON) || (fabs(dX2 - dX3) <= EPSILON)) ||
((fabs(dY2 - dY1) <= EPSILON) || (fabs(dX2 - dX1) <= EPSILON)) ) return dX2; // Solución "inexacta" a nivel EPSILON
// Obtiene nuevo X aplicando la parábola en curso
dX = 0.0;
dY = dF;
er = SRRCF_AJCUAD(dX1, dY1, dX2, dY2, dX3, dY3, &dX, &dY);
if (er) return SRRCU_FINVRF(dF, dX1, dX3, F); // Resuelve fallo cuadrático
// Y = F(X) asociado
dY = (*F)(dX);
dif = fabs(dF - dY);
// Prueba principal
if (dif <= EPSILON) return dX; // Solución "exacta" a nivel EPSILON
// El nuevo ciclo debe tomar los 3 puntos más próximos, según el esquema
// . -> ó . ->
if ((dX1 <= dX) && (dX <= dX2) && (dX2 <= dX3))
dX3 = dX2;
dY3 = dY2;
dX2 = dX;
dY2 = dY;
if ((dX3 >= dX) && (dX >= dX2) && (dX2 >= dX1))
// Realiza la prueba de salvaguarda de algoritmo fallido
dif =fabs(dX - dX2);
if (dif <= EPSILON * 10.0) return (dX+dX2)/2.0; // Solución "aproximada" a nivel epsilon
// Algoritmo fallido, devuelve regula falsi lineal
return SRRCU_FINVRF(dF, dX1, dX3, F);
return SRRCU_FINVRF(dF, dX1, dX3, F); // Resuelve solución muy inexacta
// Función: SRRCU_VEXI
// Descripción: Calcula valor extremo izquierdo a utilizar en
// SRRCU_FINVMU si rango inicial izquierdo degenerado
// ------- Uso interno no exportable ---------------
// (I) dA: Valor extremo izdo. de partida
// (I) dB: Valor extremo dcho. de partida
// (I) F : Función de cálculo que se examina
// Retorno: Valor extremo izdo. a utilizar
double SRRCU_VEXI(double dA, double dB, double (*F)(double X))
int i=0; // Contador de do
double dX1=0.0; // Valor de X-1
double dX2=0.0; // Valor de X-2
double dY1=0.0; // Valor de Y = F(X1)
double dY2=0.0; // Valor de Y = F(X2)
// Ciclo de ajuste
for (i=0;i<200;++i)
dX2 = (dX2 + dB) / 2.0;
// Rango válido
if (dY1 != dY2) return dX1;
return dB/2.0;
// Función: SRRCU_VEXD
// Descripción: Calcula valor extremo derecho a utilizar en
// SRRCU_FINVMU si rango inicial derecho degenerado
double SRRCU_VEXD(double dA, double dB, double (*F)(double X))
double dX3=0.0; // Valor de X-3
double dY3=0.0; // Valor de Y = F(X3)
dX2 = (dX2 + dA) / 2.0;
if (dY2 != dY3) return dX3;
return dA + (dB - dA)/2.0;
La lista completa de prototipos de SRRCU es
// SRRCU.h: Cabecera SERVICIO de Utilidades
// Zona de prototipos propiamente dicha
// El término __declspec(dllexport) se precisa para indicar que es una función
// exportable por la DLL C++. En C puro no procede. Para un programa de servicio
// en ILE C se indicaría en el fuente de enlace correspondiente.
__declspec(dllexport) long SRRCU_BS(const void *d, int iSize, long nSd, const long *Si, const void *Sd, int *iComp);
__declspec(dllexport) long SRRCU_BSDOUBLE(double d, long nSd, const double *Sd, short int *iComp);
__declspec(dllexport) long SRRCU_BSEARCH(const void *d, int iSize, long nSd, const void *Sd,int *iComp);
__declspec(dllexport) double SRRCU_FINNEW(double dF, double dA, double dB, double (*F)(double X), double (*FD)(double X));
__declspec(dllexport) double SRRCU_FINNEWT(double dF, double dA, double dB, double (*F)(double X));
__declspec(dllexport) double SRRCU_FINVER(double dF, double dA, double dB, double (*F)(double dX));
__declspec(dllexport) double SRRCU_FINVMU(double dF, double dA, double dB, double (*F)(double dX));
__declspec(dllexport) double SRRCU_FINVRF(double dF, double dA, double dB, double (*F)(double dX));
__declspec(dllexport) double SRRCU_ROUND(double dImpo);
Hemos hecho una panorámica de esta lista de funciones, pero nos hemos dejado en el tintero la última, la rutina auxiliar de redondeo, que por ser muy breve incluimos al completo:
// Función: SRRCU_ROUND
// Descripción: Redondea un double usando floor(entero anterior) o ceil(posterior)
// (I) dImpo: Importe a redondear [Sean por ejemplo 5.75 y 7.33]
// Retorno: Importe redondeado
double SRRCU_ROUND(double dImpo)
double dFloor = 0.0; // Valor de floor (Entero anterior)
double dResto = 0.0; // Resto de floor
dFloor = floor(dImpo); [Serían 5 y 7 en los ejemplos]
dResto = dImpo - dFloor; [A su vez serían .75 y .33 en los ejemplos]
// Devuelve el entero anterior o posterior que resulte más cercano al dado
if (fabs(dResto) < 0.5)
return dFloor; [.75 Se saltaría, .33 no => devolvería 7 para 7.33]
return ceil(dImpo); [Devuelve 6 para 5.75]
El resto de la codificación de SRRCU se puede consultar en el apéndice de fuentes.
C.1.2 El programa de servicio SRRCF. Utilidades Genéricas de Cálculo Financiero
Destaca el ajuste cuadrático utilizado por SRRCU y su alternativa más débil de ajuste lineal. Los prototipos de ambas funciones son:
short int SRRCF_AJCUAD(double x0, double y0, double x1, double y1, double x2, double y2, double *x, double *y);
short int SRRCF_AJLIN(double x0, double y0, double x1, double y1, double *x, double *y);
En la rutina SRRCF_AJCUAD se opta por resolver la ecuación cuadrática tanto en X como en Y acudiendo a la formulación directa.
Es una alternativa a utilizar las rutinas de inversión comentadas antes.
Puede observarse que aunque la formulación garantiza el resultado directo, y por eso se ha optado por su implementación, resulta más engorroso de programar que el cálculo por aproximación sucesiva que ejecuta la familia SRRCU_FINxxx, al tener que considerar explícita y exhaustivamente las diversas alternativas de detalle que surgen al contemplar las variantes de presentación de los coeficientes.
Es importante hacer notar que en la práctica nunca nos encontramos directamente con Y = A + B.X + C.X**2 si no con puntos (x0,y0) (x1,y1) (x2,y2) sobre los que hay que construir una parábola F con la que poder ajustar un y = F(x) o un
x = F (y), además también hay que tener en cuenta que F debe considerarse una instancia de aproximación de una función general G para la que coincide en los tres puntos dados.
Entonces SRRCF dispone de una rutina común para construir tal parábola:
// FUNCION....: SRRCF_AJCUADCOM
// DESCRIPCION: Calculo de los parámetros necesarios para el ajuste CUADRATICO por el método de Newton-Gregory
// FORMULA....: Dados los puntos (x0,y0), (x1,y1), (x2,y2)
// el módulo obtiene los parámetros necesarios para el
// ajuste CUADRATICO por el método de Newton_Gregory:
// Esto es, dados | x0 x1 x2
// | y0 y1 y2
// Calcula: -------- - c1
// (y1-y0) (x2-x1)
// c0=y0 c1=------- c2=--------------
// (x1-x0) (x2-x0)
// SI NO ES POSIBLE EL AJUSTE, SE DUELVE ERROR Y DEBERIA INTENTARSE ENTONCES UN AJUSTE LINEAL
// (entrada) (x0 y0) (x1 y1) (x2 y2)
// (salida) c0 c1 c2
// 0: OK
// 1: Error: Sistema irresoluble por degradación.
short int SRRCF_AJCUADCOM(double x0, double y0, double x1, double y1, double x2, double y2, double *c0, double *c1, double *c2)
*c0=y0;
// Control de sistema irresoluble por degradación (no son 3 puntos)
if ((x1==x0)||(x2==x0)||(x2==x1)) return 1;
(*c1) = (y1-y0) / (x1-x0);
(*c2) = ( (y2-y1) / (x2-x1) - (*c1) ) / (x2-x0);
// Si C2=0 se trata de un sistema lineal
if (!(*c2)) return 1;
Con esta herramienta ya se puede acometer el ajuste en x o en y, que vamos a seguir con un ejemplo numérico algo extremo para ilustrar el conocido como problema de cancelación:
// Función: SRRCF_AJCUAD
// Descripción: Cálculo de x ó y en y = A + Bx + Cx2
// [ y = 1 + 101010101010.101x + x2 ]
// Parámetros de entrada:
// 3 puntos de la parábola: (x0, y0); (x1, y1); (x2, y2)
// [ (-1, -101010101008.101); (0, 1); (1, 101010101012.101) ]
// Parámetros de entrada/salida:
// El punto solicitado (x, y) [ (0.0000000001, 11.10101010101010000001) ]
// En la versión implementada, si x<>0 se calcula y e inversamente;
// Si se precisara una versión en que 0 fuera relevante, habría que incluir un parámetro de control de solicitud
// Un valor de retorno distinto de cero indica error de proceso
// en ILE C se indicaría en el fuente de enlace correspondiente, como puede verse en el blog hermano RPG Ficheros virtuales
short int SRRCF_AJCUAD(double x0, double y0, double x1, double y1, double x2, double y2, double *x, double *y)
double c0=0.0, c1=0.0, c2=0.0; // Coeficientes del algoritmo de resolución
short int er=0; // Control de errores
// Cálculo de coefientes. Proceso común al calculo de x ó y
er = SRRCF_AJCUADCOM(x0,y0,x1,y1,x2,y2,&c0,&c1,&c2);
[ c0 = y0 = -101010101008.101
c1 = (y1-y0) / (x1-x0) = ( 1-(-101010101008.101) ) / (0 –(-1)) = 101010101009,101
c2 = ( (y2-y1) / (x2-x1) - c1 ) / (x2-x0) =
( (101010101012.101 – 1) / (1 – 0) - 101010101009,101 ) / (1-(-1)) = 1 ]
// En caso de error, se intenta ajuste lineal
er=SRRCF_AJLIN(x1,y1,x2,y2,x,y);
// Calcula el x ó y solicitados
if ( (*y) ) er = SRRCF_AJCUADX(x0,y0,x1,y1,x2,y2,c0,c1,c2,*y,x);
else er = SRRCF_AJCUADY(x0,x1,c0,c1,c2,*x,y);
[ (?, 11,10101010101010000001) -> (0.0000000001, 11,10101010101010000001) ]
[ (0.0000000001, ? ) -> (0.0000000001, 11,10101010101010000001) ]
En donde, además de llamar a la rutina común de construcción de la parábola, se controla la ejecución del ajuste de y, que vendría dado por
// FUNCION....: SRRCF_AJCUADY
// DESCRIPCION: Calculo de y segun y=A+Bx+Cx2
// FORMULA....: x0, x1, c0, c1, c2 segun SRRCF_AJCUADCOM
// (entrada) x0 x1 | c0 c1 c2
// (entrada) x
// (salida) y
short int SRRCF_AJCUADY( double x0, double x1, double c0, double c1, double c2, double x, double *y)
(*y) = c0 + c1*(x-x0) + c2*(x-x0)*(x-x1);
[ -101010101008.101
+101010101009,101 * (0.0000000001–(-1) )
+1*(0.0000000001–(-1)) * (0.0000000001 – 0)
=11,10101010101010000001 ]
Y del ajuste en X, mucho más largo, presentamos su cuerpo principal y la selección efectiva del x de respuesta de entre x01 y x02, siguiendo la solución del caso más habitual. El código para situaciones especiales puede consultarse en el anexo de fuentes.
// FUNCION....: SRRCF_AJCUADX
// DESCRIPCION: Calculo de x dado y=A+Bx+CX**2
// FORMULA....:
// Es Y = A + B.X + C.X**2
// Con A = (C0 - C1X0 + C2X0X1)
// B = (C1 - C2(X0+X1))
// C = C2
// Si B es +
// X01 = ( -B - SQRT(B**2 - 4.C.(A-Y) )/ 2.C C *NE 0
// X02 = -B/C - X01$
// Si B es -
// X01 = ( -B + SQRT(B**2 - 4.C.(A-Y) )/ 2.C C *NE 0
// Con ello se evita el "Problema de Cancelacion"
// => X01 = (Y - A) / B C=0 B *NE 0
// X02 = *ALL'9'
// => X01 = (Y - A) C=0 B *EQ 0
// -SE ELIGE X0 COMO AQUEL X0i QUE ESTE DENTRO DEL INTERVALO
// DE CALCULO: ( X1_X3 ) SEGUN EL ORDEN DE LOS PARAMETROS
// (entrada) (x0, y0) (x1, y1) (x2, y2) | c0 c1 c2 | y
// (salida) x
// 1: no se encontró x adecuada
short int SRRCF_AJCUADX(double x0, double y0, double x1, double y1, double x2, double y2,
double c0, double c1, double c2, double y, double *x)
double a,b,c,x01,x02,raiz; // Coeficientes, protosoluciones y raíz central
// Calculo de a, b, c de Y = aX**2 + bX + c
a = c0 - c1*x0 + c2*x0*x1;
[a = -101010101008.101 - 101010101009,101*(-1) + 1*(-1)*0 = 1]
b = c1 - c2*(x0+x1);
[b = 101010101009,101 – 1*(-1+0) = 101010101010,101]
[c = 1]
// Calculo de x01, x02
x02 = 9999999999999999999999999999.0;
if (!b) x01 = (y0 - a);
else x01 = (y0 - a) / b;
raiz = fabs(b*b - 4*c*(a-y));
[ raiz = 101010101010,101^2 – 4*1*(1-11,10101010101010000001) = 10203040506070807060544,43424140404040000004]
if (b>0) x01 = ( -b - sqrt(raiz) )/(2.0*c);
else x01 = ( -b + sqrt(raiz) )/(2.0*c);
[ x01 = (-101010101010.101 – 10203040506070807060544.43424140404040000004 ^.5)/2 = -101010101010.1010000001 ]
x02 = -b/c - x01;
[ x02 = -101010101010,101/1 – (-101010101010.1010000001) = 0.0000000001 ]
[ En este caso, si eligiéramos directamente (–b + sqrt(raiz)) / 2*c , obtendríamos teóricamente la misma solución, pero surge “el
problema de la cancelación”, pues se restan cantidades parecidas y el error de redondeo e puede ser más que significativo
frente al valor real.
De hecho, el ejemplo que se muestra sólo puede construirse utilizando la propia calculadora de precisión extendida que se
introdujo en el libro, pues en una calculadora normal las cifras serían indistinguibles
x02 = (-101010101010,101 + 10203040506070807060544.43424140404040000004 ^.5)/2 =
(-101010101010,101 + 101010101010,1010000002 + e)/2 = 0,0000000001 + e
x01 = -b/c - x02 = -101010101010,101/1 – 0.0000000001 = -101010101010,1010000001 ]
// Distribución de Salida de x01 ó x02 para el caso que la parábola F construida sea el resultado del ajuste de una función
// de partida G de tipo:
// Decreciente (Lo habitual en consultas financieras Precio-Tasa)
// Creciente (Consultas inversas [De pasivo]}
if ( ((y0 >= y1) && (y1 >= y2)) || ((y0 <= y1) && (y1 <= y2)) )
// Filtro "dentro de rango" si y0>y1>y2 . ó . si y0<y1<y2
// --- ---
if (enrangoxy(x0,y0, x2,y2, x01,y))
(*x) = x01;
if (enrangoxy(x0,y0, x2,y2, x02,y))
(*x) = x02; [Sería la elegida en el ejemplo, al mantener el rango]
// Para el resto de casos especiales se puede consultar el fuente en el anexo final
En caso de no poder aplicar el ajuste cuadrático, se acude al ajuste lineal desarrollado en el módulo siguiente que vamos a seguir también con un ejemplo numérico:
// FUNCION: SRRCF_AJLIN
// DESCRIPCION: Calculo de x ó y en y = A + Bx
// 2 puntos de la recta: (x0, y0); (x1, y1); [(2,2); (10,5)]
// El punto solicitado (x, y) [ (4, 2.75) ]
short int SRRCF_AJLIN(double x0, double y0, double x1, double y1, double *x, double *y)
double c0=0.0, c1=0.0; // Coeficientes del ajuste
// Filtro puntos alineados, rectas constantes paralelas a los ejes
if ( (*y) && (x0 == x1) )
(*x) = x0;
if ( (*x) && (y0 == y1) )
(*y) = y0;
// Proceso común al calculo de x ó y. Cálculo de coeficientes
er = SRRCF_AJLINCOM(x0,y0,x1,y1,&c0,&c1); [(2,2);(10,5) à 2, 0.375]
// Control del error
if ( (*y) ) (*x)=(*y); // Si dado y, devolvemos x=y
else (*y)=(*x); // Si no dado y, devolvemos y=x
// Ajusta en X ó Y según solicitud
if ( (*y) ) er = SRRCF_AJLINX(x0,c0,c1,*y,x); [(4, ? ) -> (4, 2.75) ]
else er = SRRCF_AJLINY(x0,c0,c1,*x,y); [(?, 2.75 ) -> (4, 2.75) ]
Las rutinas auxiliares utilizadas son mucho más sencillas que las involucradas en el caso cuadrático, reflejando la naturaleza intrínseca más simple del problema:
// FUNCION....: SRRCF_AJLINCOM
// DESCRIPCION: Calculo de los parámetros necesarios para el
// ajuste LINEAL por el método de Newton-Gregory
// FORMULA....: Dados los puntos (x0,y0) y (x1,y1) [(2,2); (10,5)]
// Calcula
// 1) c0=y0 [ 2 ]
// (y1-y0)
// 2) c1= ------- [ 0.375 ]
// (x1-x0)
// Entrada: Puntos (x0, y0); (x1, y1)
// Salida: Coeficientes c0 c1
short int SRRCF_AJLINCOM( double x0, double y0, double x1, double y1, double *c0, double *c1)
*c0=y0; [c0 = 2]
// Filtra error de sistema irresoluble por degradación (no son 2 puntos)
if(x1==x0) return 1;
// Aplica la formulación
(*c1) = (y1-y0) / (x1-x0); [ (5-2) / (10-2) = 3/8 = 0.375 ]
// Si C1=0 se trata de una paralela al eje y también devuelve error
if ((*c1)==0) return 1;
Finalmente, las rutinas de ajuste específico en X ó en Y son:
// FUNCION....: SRRCF_AJLINX
// DESCRIPCION: Calculo de x en y=A+Bx
// FORMULA....: Dados los coeficientes de Newton-Gregory c0,c1 se calcula
// a=c0-(c1x0) [ 1.25 ]
// b=c1 [ 0.375 ]
// => x=(y-a)/b (si b!=0)
// => x=(y-a) (si b==0)
// (entrada) x0 c0 c1 | y dados
// (salida) x buscado
short int SRRCF_AJLINX(double x0, double c0, double c1, double y, double *x)
// Calculo de a y b
a=c0-(c1*x0); [ 2 – (0.375*2) = 1.25 ]
b=c1; [ 0.375 ]
// Cálculo de x
if (!b) (*x)=(y-a);
else (*x)=(y-a)/b; [ (2.75 – 1.25) / 0.375 = 4 ]
// FUNCION....: SRRCF_AJLINY
// DESCRIPCION: Calculo de y segun y=A+Bx
// directamente y = c0 + c1 * (x-x0)
// (entrada) x0 c0 c1 | x dados
// (salida) y buscado
short int SRRCF_AJLINY( double x0, double c0, double c1, double x, double *y)
(*y) = c0 + c1 * (x-x0); [ 2 + 0.375 * (4 – 2) = 2.75 ]
C.1.3 SRRCAL Utilidades de conversión alfanumérica.
Básicamente, son rutinas interfaz a sprintf que permiten convertir números a cadenas.
Veamos por ejemplo la rutina de conversión de long a alfa
// srrcal.cpp : Defines the initialization routines for the DLL.
// Variable global asociada
static char cLTOAL[] = "0000000000"; // Resultado SRRCAL_ltoal
// FUNCION....: SRRCAL_ltoal
// DESCRIPCION: Convierte 1 long a caracter[11]
// (entrada) lNUM: Número a convertir
// RETORNO....: Número convertido
char* SRRCAL_ltoal(long lNUM)
char cBuffer[] = " "; // Buffer de trabajo
short int ilong = 0; // Longitud del Buffer
// Inicializa resultado [0000000000]
strcpy(cLTOAL, "0000000000");
// Conversión efectiva [Supongamos 123456, longitud 6]
sprintf(cBuffer, "%d", lNUM);
ilong = strlen(cBuffer);
// [10-6=
// Vuelca conversión a resultado 0000123456]
memcpy(cLTOAL + 10 - ilong, cBuffer, ilong);
// Devuelve resultado obtenido
return cLTOAL; // [0000123456]
Veamos ahora un ejemplo de uso.
En el programa de llamada, de un lado definiremos un puntero auxiliar para recibir el resultado, así en la calculadora extendida PSRRCE tenemos
static char *cLTOA; // Soporte de SRRCAL_ltoal para conversión alfanumérica
también tendremos definición(es) para pasar el resultado, como por ejemplo
static char cCont[] = "0000000000"; // Paso de resultado de SRRCAL_ltoal(lCont)
esto hay que hacerlo porque cada invocación de SRRCAL_ltoal reemplazará cLTOA, tiene por tanto un carácter volátil, por lo que tras invocar a la función conviene fijar su resultado en una variable permanente, como sigue:
strcpy(cCont, cLTOA);
Para presentar más muestras de uso de las rutinas de conversión, se va a mostrar ahora la rutina de salida de la calculadora extendida PSRRCE.
Esta rutina se encarga de depurar las numeraciones perdidas en el fichero virtual histórico de datos debidas a la supresión manual de ítems, justo antes de proceder a su salvado a disco. En esto difiere de otras rutinas de cierre que simplemente invocan al salvado a disco SRRCW_SAVF sin más.
En la codificación, el papel de variables auxiliares de paso lo juegan las propias variables char[] del diseño de la estructura de entrada/salida asociada al fichero virtual “PSRRCE”.
Se utiliza también la variable global citada antes:
Por su parte, las estructuras de soporte del fichero virtual “PSRRCE” son:
// Estructura de persistencia de datos
// Subestructura de claves
struct sK {char cCont[11];}; // Contador
// Subestructura de datos, histórico del cálculo
char cA[1001]; // Valor Operando A
char cB[1001]; // Valor Operando B
char cC[1001]; // Valor Resultado C
char cM[1001]; // Valor Resultado Memoria
char cOP[11]; // Operación + - * / 1/A ...
char cStamp[24]; // Stamp
// Ds del fichero virtual de persistencia de datos
struct Psrrce_dat
struct sK sDk; // Claves
struct sD sDd; // Datos
} Psrrce_reg; // Registro de soporte
Y el código propiamente dicho, ejemplo de uso de ciclos de lectura y proceso de ficheros virtuales, así como de la rutina de conversión SRRCAL_ltoal es:
void CPsrrceDlg::OnCancel()
short int er = 0; // Control de errores SRRCW
long lresul = 0; // Control de resultados SRRCW
// Evalua nºitems en PSRRCE
er = SRRCW_INF("PSRRCE", &iDimC, &lDimD, &lNITEM, &lBAJAS, &lNIDD);
m_NREG = lNITEM - lBAJAS;
// Depura numeración volcando PSRRCE a “PSRRCE+1000000000”, releyéndolo y renumerándolo
if (m_NREG)
for(l=1;l<=m_NREG;++l)
// Resitua en comienzo de datos (Clave 0’s)
cLTOA = SRRCAL_ltoal(0);
strcpy(Psrrce_reg.sDk.cCont, cLTOA);
lresul = SRRCW_SETLL("PSRRCE", &Psrrce_reg);
// Lee y elimina datos origen (que se salvan a justo a continuación)
er = SRRCW_READ("PSRRCE", 1, &Psrrce_reg);
if (er) break;
lresul = SRRCW_DELETE("PSRRCE", &Psrrce_reg);
// Duplica a clave temporal “+1000000000”
cLTOA = SRRCAL_ltoal(atol(Psrrce_reg.sDk.cCont)+1000000000);
lresul = SRRCW_WRITE("PSRRCE", &Psrrce_reg);
// Resitua en clave temporal
cLTOA = SRRCAL_ltoal(1000000000);
// Lee y elimina clave temporal (que se salva justo a continuación)
er = SRRCW_READ("PSRRCE", lresul, &Psrrce_reg);
// Duplica a clave secuencial definitiva
cLTOA = SRRCAL_ltoal(l);
strcpy(Psrrce_reg.sDk.cCont, cCont);
// Salva el fichero virtual a fichero físico (Si es un fichero nulo, el efecto es vaciar este último)
m_NREG = SRRCW_SAVF("PSRRCE");
// Cierra el servicio de ficheros virtuales
er = SRRCW_CLOSE();
// Cierre estandar del programa
Otras rutinas que contempla SRRCAL son las dedicadas a la depuración de cadenas de entrada con el objetivo de facilitar la toma de datos desde cuadros de diálogo.
Así tenemos la siguiente relación de prototipos con sus comentarios de cabecera, de ellos SRRCAL_DepuraBlanks0 y SRRCAL_Trim ya se presentaron en el capítulo sobre punteros y el resto tiene una estructura de implementación similar:
// Función: SRRCAL_DepuraBlanks
// Descripción: Auxiliar de depuración de blancos intermedios de una
// expresión de entrada. Sin recorte de longitud.
// (IO) cValorA: Valor de expresión a depurar
// Retorno: Nºde blancos depurados
int SRRCAL_DepuraBlanks(char *cValorA)
// Función: SRRCAL_DepuraBlanks0
// expresión de entrada. Con recorte de longitud.
int SRRCAL_DepuraBlanks0(char *cValorA)
// Función: SRRCAL_DepuraCtrl
// Descripción: Auxiliar de depuración de caracteres de control intermedios en
// expresiones de entrada. Sin recorte de longitud.
// Retorno: Nºde caracteres de control depurados
int SRRCAL_DepuraCtrl(char *cValorA)
// Función: SRRCAL_Trim
// Descripción: Auxiliar de depuración de blancos de cola de una
// (IO) cText: Valor de expresión a depurar
// Retorno: Longitud tras la depuración
unsigned long SRRCAL_Trim(char *cText)
Otra función muy utilizada en los ejemplos es SRRCAL_lLexi, que se introdujo a su vez en el capítulo de estructuras, y de la que recordamos aquí su prototipo:
// SRRCAL_lLexi Lexicográfica para long's en MsC++
// Reordena un long para poder almacenarlo lexicográficamente y poder utilizar las funciones memcmp de SRRCW con él
// Cuando se le pasa dos veces seguidas, restaura el nº a su valor original
// (Entrada/Salida) l Nº que se convierte ó restaura
void SRRCAL_lLexi(long *l)
La relación nominal completa de los prototipos de SRRCAL es
__declspec(dllimport) int SRRCAL_altoi(const char *cNumero_a_convertir_p);
__declspec(dllimport) long SRRCAL_altol(const char *cNumero_a_convertir_p);
__declspec(dllimport) void SRRCAL_cEdit(char cCod_edicion, char *cNum_a_editar);
__declspec(dllimport) char *SRRCAL_CStringTochar(CString CS, int iSize);
__declspec(dllimport) int SRRCAL_ctoi(const char *cDigito_a_convertir_p);
__declspec(dllimport) int SRRCAL_DepuraBlanks(char *cTextoAdepurar);
__declspec(dllimport) int SRRCAL_DepuraBlanks0(char *cTextoAdepurarYreducir);
__declspec(dllimport) int SRRCAL_DepuraCtrl(char *cTextoAdepurar);
__declspec(dllimport) char* SRRCAL_dtoal(double dNumero_a_convertir_p);
__declspec(dllimport) char* SRRCAL_hival(int iLongitud_deseada_del_hival_p);
__declspec(dllimport) short int SRRCAL_isctrl(char c);
__declspec(dllimport) char* SRRCAL_itoc(int iDigito_a_convertir_p);
__declspec(dllimport) char* SRRCAL_itoal(int iNumero_a_convertir_p);
__declspec(dllimport) char* SRRCAL_ltoal(long lNumero_a_convertir_p);
__declspec(dllimport) void SRRCAL_lLexi(long *lNumero_a_convertir_p);
__declspec(dllimport) char *SRRCAL_lFile(const char *NombrFichroFisicoBasewFile, long NumeroSecuenciaNombreLogicoSalida);
__declspec(dllimport) unsigned long SRRCAL_Trim(char *cTextoAdepurarYreducir);
__declspec(dllimport) char *SRRCAL_wFile(void);
Como su uso es continuo, los nombres de las variables prototipo se han elegido para minimizar la posibilidad de conflicto al incorporar el fuente de cabecera prototipo en los diversos programas de uso.
Su código completo se encuentra en el anexo de fuentes.
Como son servicios básicos, se pueden ver aplicaciones de cada uno de ellos repartidos a lo largo de los diversos ejemplos presentados en el libro.
En la aplicación se utiliza para generar claves en la grabación de ítems de pilas y colas.
También se se ha utilizado en el estampillado de los registros históricos de las calculadoras de los ejemplos.
La relación completa de sus servicios es
__declspec(dllimport) long SRFECHA_AMD(long lFecha_DMA);
__declspec(dllimport) long SRFECHA_DMA(long lFecha_AMD);
__declspec(dllimport) double SRFECHA_DIFA(long lFede, long lFeha);
__declspec(dllimport) long SRFECHA_DIFF(long lFede, long lFeha);
__declspec(dllimport) long SRFECHA_FINI(long lFecha_AMD);
__declspec(dllimport) long SRFECHA_FFIN(long lFecha_AMD);
__declspec(dllimport) long SRFECHA_SAN(long lFede, long lAn);
__declspec(dllimport) long SRFECHA_SMES(long lFede, long lMes);
__declspec(dllimport) long SRFECHA_SDIAS(long lFede, long lDias);
__declspec(dllimport) long SRFECHA_DATE(void);
__declspec(dllimport) long SRFECHA_TIME(void);
__declspec(dllimport) char *SRFECHA_STAMALFA(void);
__declspec(dllimport) char *SRFECHA_STAMNUM(void);
__declspec(dllimport) char *SRFECHA_STAMNUMINV(void);
De estos ya hemos visto los estampillados numéricos y los extractores de TIME y DATE al presentar el tratamiento de pilas y colas en SRRCW pues se emplean para generar las claves de los ítems que se graban.
Vamos a presentar ahora alguna de las funciones más en detalle, particularmente las que se han utilizado en alguno de los ejemplos del libro:
// Prototipos internos
long Dias(long lFecha);
short int FAMD(long lFecha_AMD, int *iDD, int *iMM, long *lAAAA);
short int FDMA(long lFecha_DMA, int *iDD, int *iMM, long *lAAAA);
short int ValiFech(int iDD, int iMM, long lAAAA);
// Procedimientos internos
// Función: Dias
// Propósito: Obtiene días desde fecha 0, para aplicar a SRFECHA_DIFF,
// según la fórmula
// f(D) = 365*AAAA + 31*(MM-1) + DD + int(z/4) - x
// (entrada) lFecha: Fecha en formato AAAAMMD
// Días desde fecha 0
long Dias(long lFecha)
short int er = 0; // 0/1 error de invocación
int iDD = 0; // Fracción DD
int iMM = 0; // Fracción MM
long lAAAA = 0; // Fracción AAAA
double x = 0.0; // factor x
long z = 0; // factor z
long lDias = 0; // Resultado
// Fragmenta la fecha dada
er = FAMD(lFecha, &iDD, &iMM, &lAAAA);
if (er) return 0;
// Aplica la fórmula
z = lAAAA - 1;
if (iMM > 2)
x = .4 * ( (double) iMM) + 2.3;
z = lAAAA;
lDias = 365 * lAAAA + 31 * (iMM - 1) + iDD + (z/4) - int(x);
return lDias;
// Función FAMD
// Fragmenta una fecha en formato AMD en sus componentes
// (entrada) lFecha_AMD Fecha en formato AMD
// (salida) iDD Fragmento DD
// (salida) iMM Fragmento MM
// (salida) lAAAA Fragmento AAAA
// 0 OK
short int FAMD(long lFecha_AMD, int *iDD, int *iMM, long *lAAAA)
long lA0 = 0; // AAAA0000
long lMM0 = 0; // MM00
long lMD = 0; // Fracción MMDD
// Fragmentación de la fecha proporcionada
*lAAAA = lFecha_AMD / 10000;
lA0 = (*lAAAA) * 10000;
lMD = lFecha_AMD - lA0;
*iMM = lMD / 100;
lMM0 = (*iMM) * 100;
*iDD = lMD - lMM0;
// Función FDMA
// Fragmenta una fecha en formato DMA en sus componentes
// (entrada) lFecha_DMA Fecha en formato DMA
short int FDMA(long lFecha_DMA, int *iDD, int *iMM, long *lAAAA)
long lDD0 = 0; // DD000000
long lMM0 = 0; // MM0000
long lMA = 0; // Fracción MMAAAA
*iDD = lFecha_DMA / 1000000;
lDD0 = (*iDD) * 1000000;
lMA = lFecha_DMA - lDD0;
*iMM = lMA / 10000;
lMM0 = (*iMM) * 10000;
*lAAAA = lMA - lMM0;
// Función: ValiFech
// Propósito: Valida fecha dada como fragmentos DD MM AAAA
// (entrada) iDD: Fragmento DD de fecha
// iMM: Fragmento MM de fecha
// lAAAA: Fragmento AAAA de fecha
short int ValiFech(int iDD, int iMM, long lAAAA)
// Validación de rangos
if (iDD < 0) return 1;
if (iDD > 31) return 1;
if (iMM < 0) return 1;
if (iMM > 12) return 1;
if (lAAAA < 0) return 1;
if (lAAAA > 9999) return 1;
if ((iMM == 2) && (iDD > 29)) return 1;
if ((iMM == 4) && (iDD > 30)) return 1;
if ((iMM == 6) && (iDD > 30)) return 1;
if ((iMM == 9) && (iDD > 30)) return 1;
if ((iMM ==11) && (iDD > 30)) return 1;
// Bisiesto: Divisible por 4; además, los divisibles por 100 han de ser múltiplos de 400
if ((iMM == 2) && (iDD == 29))
if (lAAAA/4) return 1;
if ((lAAAA/100) && !(lAAAA/400)) return 1;
// Funciones exportables
// Función: SRFECHA_AMD
// Propósito: Valida y cambia una fecha de formato DMA a AMD
// (entrada) lFecha_DMA: Fecha en formato DDMMAAAA
// Esta función se utiliza en la calculadora hipotecaria
// Fecha en formato AAAAMMDD ó 0 si error
__declspec(dllexport) long SRFECHA_AMD(long lFecha_DMA)
long lAMD = 0; // Resultado AAAAMMDD
er = FDMA(lFecha_DMA, &iDD, &iMM, &lAAAA);
// Validación
er = ValiFech(iDD, iMM, lAAAA);
// Construye fecha de salida
lAMD = lAAAA * 10000 + iMM * 100 + iDD;
return lAMD;
// Función: SRFECHA_DMA
// Propósito: Valida y cambia una fecha de formato AMD a DMA
// (entrada) lFecha_AMD: Fecha en formato AAAAMMDD
// Fecha en formato DDMMAAAA ó 0 si error
__declspec(dllexport) long SRFECHA_DMA(long lFecha_AMD)
long lDMA = 0; // Resultado DDMMAAAA
er = FAMD(lFecha_AMD, &iDD, &iMM, &lAAAA);
lDMA = iDD * 1000000 + iMM * 10000 + lAAAA;
return lDMA;
// Función: SRFECHA_DIFF
// Propósito: Obtiene días entre fechas
// (entrada) lFede: Fecha desde en formato AAAAMMDD
// (entrada) lFeha: Fecha hasta en formato AAAAMMDD
// Diferencia de días entre fechas
__declspec(dllexport) long SRFECHA_DIFF(long lFede, long lFeha)
long lDiff = 0; // Resultado
lDiff = Dias(lFeha) - Dias(lFede);
// Función: SRFECHA_DATE
// Propósito: Devuelve fecha en curso en formato AAAAMMDD
// Parámetros: Ninguno
// Retorno: DATE solicitado
__declspec(dllexport) long SRFECHA_DATE(void)
* Esta función se presentó en el capítulo dedicado a SRRCW, sección pilas y colas
// Función: SRFECHA_TIME
// Propósito: Devuelve time en curso en formato HHMMSS
// Retorno: TIME solicitado
__declspec(dllexport) long SRFECHA_TIME(void)
Para la siguiente función se utiliza la variable global siguiente:
// Salida de SRFECHA_STAMALFA
static char cStamAlfa[] = " ";
// Función: SRFECHA_STAMALFA
// Propósito: Devuelve estampillado tipo
// "Sun Sep 03 11:53:35 2006" en cStamAlfa[25]
// Esta función se utiliza en el estampillado de los registros históricos
// de las calculadoras presentadas en el libro
// Estampillado solicitado
__declspec(dllexport) char *SRFECHA_STAMALFA(void)
// Extrae *Time de la estructura estándar del sistema
tim = time(NULL) ;
local = localtime(&tim);
// Pasa *Time a variable de salida y a continuación cierra la cadena *char
sprintf(cStamAlfa, "%s", asctime(local));
memset(cStamAlfa+24, '\x00', 1);
return cStamAlfa;
// Función: SRFECHA_STAMNUM
// "AAAAMMDDHHMMSS" en cStamNum[15]
__declspec(dllexport) char *SRFECHA_STAMNUM(void)
// Función: SRFECHA_STAMNUMINV
// "99999999999999 - AAAAMMDDHHMMSS" en cStamNum[15]
// Estampillado inverso solicitado
__declspec(dllexport) char *SRFECHA_STAMNUMINV(void)
Con ello termina la presentación de las rutinas de utilidades. Recordemos que ha sido un extracto, se invita a consultar la codificación completa en el apéndice
"C.4 Relación de programas de servicio auxiliares utilizadas por SRRCW y wFile"

References: resolución 
 resolución 
 resolución 
 Resolución 
 resolución 
 resolución 
 resolución