Serie ficheros virtuales

 

 


C Ficheros virtuales

 

 

 

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

 

 

C.1.1.1 Introducción. La búsqueda lineal y dicotómica

 

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).

                                                                              2

 

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 

 

    // Inz 

    char *cSd = (char *) Sd; // Uso de Sd como char* (Para seguimiento en depuración) 

    char *cd  = (char *) d; 

 

    // Rango inicial  

    a = 0; 

    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;  

  

 

    // Ciclo de Contracción Dicotómica  

    do 

    { 

     ab2 = (a + b) >> 1; 

  

     // Localización exacta  

 

     *iComp = memcmp(cd, cSd + (Si[ab2-1] * iSize), iSize); // Instrucción central 

     if (!*iComp) return ab2; 

  

 

     // Localización inexacta  

 

     if (ab2 <= a) return a; 

 

 

     // Compara, Divide Rango y Continua Ciclo de Proceso   

 

     if (*iComp > 0) a = ab2; else b = ab2; 

    } 

    while (a < b); 

 

    return ab2; 

   }        

            

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                                      */ 

   /*                                                                 */ 

   /* DESCRIPCION: Localiza en una serie ascendente un item           */ 

   /*              (Orientada al uso en SRRCM, versión inicial)       */ 

   /*                                                                 */ 

   /* PARAMETROS.:                                                    */ 

   /* (I)          d: Dato a localizar                                */ 

   /*         iSizep: Tamaño del dato                                 */ 

   /*            nSd: Dimensión efectiva de la serie de datos         */ 

   /*             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_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 

  

    // Inz 

    char *cSd = (char *) Sd; // Uso de Sd como char* (Para seguimiento en depuración) 

    char *cd  = (char *) d; 

 

    // Rango inicial  

    a = 0; 

    b = nSd; 

   

 

    // Valor extremo inferior  

 

    *iComp = memcmp(cd, cSd, iSize); 

    if (*iComp<0)  return a; 

    if (!*iComp)   return 1; 

 

 

    // Valor extremo superior  

 

    *iComp = memcmp(cd, cSd + ((b-1) * iSize), iSize); 

    if (*iComp>=0) return  b;  

  

 

    // Ciclo de Contracción Dicotómica  

    do 

    { 

     ab2 = (a + b) >> 1; 

  

 

     // Localización exacta  

 

     *iComp = memcmp(cd, cSd + ((ab2-1) * iSize), iSize);  // Instrucción central 

     if (!*iComp) return ab2; 

  

 

     // Localización inexacta  

 

     if (ab2 <= a) return a; 

 

 

     // Compara, Divide Rango y Continua Ciclo de Proceso  

 

     if (*iComp > 0) a = ab2; else b = ab2; 

    } 

    while (a < b); 

 

    return ab2; 

  

 

                                                                                                         ________


 

C.1.1.2 La resolución de ecuaciones. Métodos Newton y Cuasi-Newton

 

Pero si la búsqueda corresponde realmente a la resolución de una ecuación 

 

 -1

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

 

  

                F(X ) 

                   i 

  X   = X  -  ------- 

   i+1   i     F’(X ) 

                   i 

 

 

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

                 F(X)

 

 

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.

 

                                                                                                         ________

 

C.1.1.3 Implementació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

 

   dCuota = SRRCU_FINVMU(EPSILON, dCuota1, dCuota2, PrCuadro);

 

 

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:

 

//-----------------------------------------------------------------------------

// SRRCH_i_ani

//

// Cálculo del tanto de interés

//

// Partiendo de la formulación

//

// ani = sni / (1+i)^n

//

// 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

//

// Parámetros:

//

// (I) ani = Valor actual de una renta postpagable

// (I) i = Interés aplicable (En cada periodo)

//

// Retorno: i o *ZEROS en caso de error

//

//----------------------------------------------------------------------------

double SRRCH_i_ani(double ani, long n)

{

 double di1 = 0.0; // solución inexacta inferior

 double di2 = 0.0; // solución inexacta superior

 double di = 0.0; // solución buscada, valor de retorno

 

 

 // Filtro

 

 if ( (ani <= 0.0) || (n <= 0) ) return 0.0;

 

 

 // Paso de parámetros globales a la rutina Prani

 

 N = n;

 

 

 // 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

 

 di = SRRCU_FINVMU(ani, di1, di2, Prani);

 

 return di;

}

 

 

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 Prani(double i)   

   { 

     double dResul = 0.0; // Variable retorno (Se explicita para facilitar seguimiento por debug) 

 

    dResul = SRRCH_ValorActualRenta_ani(N, i); 

 

    return dResul; 

  

 

                                                                                                         ________

 

 

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

 

 

double SRRCH_ValorAdquiridoRenta_sni(long n, double i);

 

Valor final de la renta postpagable asociada:

 

sni = ( (1+i)^n - 1 ) / i

 

i = 0.02 n = 12  ->  sni = 13.412089728127

 

 

double SRRCH_ValorActualRenta_ani(long n, double i);

 

Valor actual de la renta postpagable asociada:

 

ani = sni / (1+i)^n  ->  ani = 10.575341220917

 

 

 

double SRRCH_TerminoDeRenta_Pni(long n, double i);

 

Calcula el término periodico de una renta renta postpagable, necesario para obtener el valor final sni = 1

 

Pni = 1 / sni  ->  Pni = 0.074559596622951

 

 

 

double SRRCH_TerminoDeRenta_Ani(long n, double i);

 

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

 

 

Que se aplica directamente para obtener la cuota

 

       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

 

 

1ª Parábola

 

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

 

 

2ª Parábola

 

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

dX3 = (Sin cambios)  = 0.094559596622951506          dY3 =  6.9991442356960345

 

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

 

                                                                                                         ________

 

C.1.1.3.2 Implementación del algoritmo Müller

 

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 

// 

// Parámetros: 

// (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 

  

 // Rango inicial 

 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; 

  dY1 = (*F)(dX1); 

  dY2 = (*F)(dX2); 

 

 if (dY2 == dY3) 

 

  dX3 = SRRCU_VEXD(dA, dB, F); // Su  codificación se expone más adelante 

  dX2 = (dX3 + dA) / 2.0; 

  dY2 = (*F)(dX2); 

  dY3 = (*F)(dX3); 

 

 

 // 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 

  //       .         .                  .        .       

  //        y         .                y        .       

  //         .         .              .        .            

  //          .  ->           ó      .     ->   

  //       -x--       ---            --x-       ---      

     

  if ((dX1 <= dX) && (dX <= dX2) && (dX2 <= dX3)) 

 

   dX3 = dX2; 

   dY3 = dY2; 

   dX2 = dX; 

   dY2 = dY

 

  else 

 

   if ((dX3 >= dX) && (dX >= dX2) && (dX2 >= dX1)) 

  

    dX1 = dX2; 

    dY1 = dY2; 

    dX2 = dX; 

    dY2 = dY; 

  

   else 

  

     

    // 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 --------------- 

// 

// Parámetros: 

// (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) 

 

 // Rango  inicial 

 dX1 = dA; 

 dX2 = dA + (dB - dA)/10.0; 

 

 // Ciclo de ajuste 

 for (i=0;i<200;++i) 

 

  dX1 = dX2; 

  dX2 = (dX2 + dB) / 2.0; 

  dY1 = (*F)(dX1); 

  dY2 = (*F)(dX2); 

 

  // 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 

// 

//  ------- Uso interno no exportable --------------- 

// 

// Parámetros: 

// (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_VEXD(double dA, double dB, double (*F)(double X)) 

 int i=0;      // Contador de do 

 double dX2=0.0; // Valor de X-2 

 double dX3=0.0; // Valor de X-3 

 double dY2=0.0; // Valor de Y = F(X2) 

 double dY3=0.0; // Valor de Y = F(X3) 

 

 // Rango  inicial 

 dX2 = dA + (dB - dA)/10.0; 

 dX3 = dB; 

 

 // Ciclo de ajuste 

 for (i=0;i<200;++i) 

 

  dX3 = dX2; 

  dX2 = (dX2 + dA) / 2.0; 

  dY2 = (*F)(dX2); 

  dY3 = (*F)(dX3); 

 

  // Rango válido 

  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) 

   // 

   // Parámetros: 

   // (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

 -1

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                                                  

    //                                     (y2-y1)                      

    //   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

    //                                                                     

    // PARAMETROS.:                                                        

    //   (entrada) (x0 y0) (x1 y1) (x2 y2)                                                                          

    //   (salida)   c0 c1 c2 

    //                                                                     

    // RETORNO....:                                                        

    //                 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; 

     return NO_ERRO;                                                       

   

 

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 

   // 

   // 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, 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  

  

    if (er)                                                             

   

     er=SRRCF_AJLIN(x1,y1,x2,y2,x,y);                                      

     return  er;                                                           

    }                                                                      

             

    // 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)  ]                                    

                                                                    

    return er;                                                       

   }      

                      

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                       

  //                                                                     

  //                                                                     

  // PARAMETROS.:                                                        

  //   (entrada)  x0 x1  |  c0 c1 c2                                                     

  //   (entrada)  x                                                  

  //   (salida)   y                                                     

  //                                                                     

  // RETORNO....:                                                        

  //                 0: OK                                               

  //                                                                     

  //-----------------------------------------------------------------    

  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  ] 

 

   return NO_ERRO;                                                       

  } 

 

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           

  //      X02  = -B/C - X01$                                             

  //                                                                     

  //    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          

  //      X02  = *ALL'9'                                                 

  //                                                                     

  //                                                                     

  //  -SE ELIGE X0   COMO AQUEL X0i QUE ESTE DENTRO DEL INTERVALO        

  //   DE CALCULO:  ( X1_X3 )  SEGUN EL ORDEN DE LOS PARAMETROS          

  //                                                                  

  //                                                                  

  // PARAMETROS.:                                                        

  //   (entrada)  (x0, y0) (x1, y1) (x2, y2)  |  c0 c1 c2  |  y                                              

  //   (salida)   x  

  //                                                                  

  // RETORNO....:                                                     

  //                 0: OK                                            

  //                 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 = c2; 

  [c = 1] 

 

   // Calculo de x01, x02   

   if (!c) 

   

    x02 = 9999999999999999999999999999.0; 

    if (!b) x01 = (y0 - a); 

    else    x01 = (y0 - a) / b; 

   

   else 

  

    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; 

     return NO_ERRO; 

    

 

    if (enrangoxy(x0,y0, x2,y2, x02,y)) 

    

     (*x) = x02;      [Sería la elegida en el ejemplo, al mantener el rango]     

     return NO_ERRO; 

    }  

  

   // Para el resto de casos especiales se puede consultar el fuente en el anexo final

 

   // . . . 

 

   return NO_ERROR; 

  } 

                                                                                                         ________

 

 

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)] 

   // 

   // Parámetros de entrada/salida: 

   // 

   //              El punto solicitado (x, y)   [ (4, 2.75) ] 

   // 

   // 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 

   // 

   //-------------------------------------------------------------------- 

   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                                                          

     short int er=0;        // Control de errores 

   

  

     // Filtro puntos alineados, rectas constantes paralelas a los ejes 

 

     if ( (*y) && (x0 == x1) ) 

     { 

      (*x) = x0; 

      return NO_ERRO; 

    

 

     if ( (*x) && (y0 == y1) ) 

     { 

      (*y) = y0; 

      return NO_ERRO; 

     } 

 

 

     // 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 (er)                                                             

     {                                                                      

      if ( (*y) ) (*x)=(*y); // Si dado y, devolvemos x=y                                                      

      else        (*y)=(*x); // Si no dado y, devolvemos y=x               

      return er;                                                          

     }                                                                      

      

 

     // 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) ]                                    

                                                                          

     return er;                                                       

    }                                                        

                                                                                                         ________

 

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)                                         

   //                                                                     

   // PARAMETROS.:     

   // 

   //  Entrada: Puntos (x0, y0); (x1, y1) 

   //  Salida: Coeficientes c0 c1 

   // 

   //                                                                     

   // RETORNO....:                                                        

   //                 0: OK                                               

   //                 1: Error: Sistema irresoluble por degradación.      

   //                                                                     

   //----------------------------------------------------------------- 

   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;      

                                 

    return NO_ERRO;                                                       

  

                                                                          

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)                             

  //                                                                     

  //                                                                     

  // PARAMETROS.:                                                        

  //   (entrada)  x0 c0 c1  |  y   dados 

  //   (salida)   x buscado 

  //                                                                     

  // RETORNO....:                                                        

  //                 0: OK                                               

  //                                                                     

  //----------------------------------------------------------------- 

  short int SRRCF_AJLINX(double x0, double c0,  double c1, double y, double *x)                                           

  {                                                                        

   double a,b;                                                            

                       

                                                    

   // 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 ]                                                      

                                                                          

   return NO_ERRO;                                                       

  }                                                                        

                                                                          

  //----------------------------------------------------------------- 

  // FUNCION....: SRRCF_AJLINY                                           

  //                                                                     

  // DESCRIPCION: Calculo de y segun y=A+Bx                               

  //                                                                     

  // FORMULA....: Dados los coeficientes de Newton-Gregory c0,c1 se calcula 

  //              directamente y = c0 + c1 * (x-x0) 

  //                                                                     

  // PARAMETROS.:                                                        

  //   (entrada)  x0 c0 c1  |  x   dados 

  //   (salida)   y buscado 

  //                                                                     

  // RETORNO....:                                                        

  //                 0: OK                                            

  //                                                                     

  //-----------------------------------------------------------------    

  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 ] 

                                                                          

   return NO_ERRO;                                                       

  }                                                                      

                                                                                                          ________

 

 

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]

//

// PARAMETROS.:

// (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=

 //                               01234

 // 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

 

 

// Variables globales

 

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:

 

...

static char *cLTOA; // Soporte de SRRCAL_ltoal para conversión alfanumérica

...

 

 

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

 

struct sD

{

 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()

{

 // TODO: Add extra cleanup here

 

 short int er = 0; // Control de errores SRRCW

 long l = 0; // Contador de for

 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);

 

   strcpy(Psrrce_reg.sDk.cCont, cLTOA);

 

   lresul = SRRCW_WRITE("PSRRCE", &Psrrce_reg);

  }

 

 

  for(l=1;l<=m_NREG;++l)

  {

 

   // Resitua en clave temporal

 

   cLTOA = SRRCAL_ltoal(1000000000);

 

   strcpy(Psrrce_reg.sDk.cCont, cLTOA);

 

   lresul = SRRCW_SETLL("PSRRCE", &Psrrce_reg);

 

 

   // Lee y elimina clave temporal (que se salva justo a continuación)

 

   er = SRRCW_READ("PSRRCE", lresul, &Psrrce_reg);

   if (er) break;

 

   lresul = SRRCW_DELETE("PSRRCE", &Psrrce_reg);

 

 

   // Duplica a clave secuencial definitiva

 

   cLTOA = SRRCAL_ltoal(l);

 

   strcpy(Psrrce_reg.sDk.cCont, cCont);

 

   lresul = SRRCW_WRITE("PSRRCE", &Psrrce_reg);

  }

 }

 

 

 // 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

 

 CDialog::OnCancel();

 

 return;

}

                                                                                                         ________

 

 

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.

//

// Parámetros:

// (IO) cValorA: Valor de expresión a depurar

//

// Retorno: Nºde blancos depurados

//--------------------------------------------------------------------

int SRRCAL_DepuraBlanks(char *cValorA)

 

 

//--------------------------------------------------------------------

// Función: SRRCAL_DepuraBlanks0

//

// Descripción: Auxiliar de depuración de blancos intermedios de una

// expresión de entrada. Con recorte de longitud.

//

// Parámetros:

// (IO) cValorA: Valor de expresión a depurar

//

// Retorno: Nºde blancos depurados

//-----------------------------------------------------------------------

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.

//

// Parámetros:

// (IO) cValorA: Valor de expresión a depurar

//

// 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

// expresión de entrada. Con recorte de longitud.

//

// Parámetros:

// (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

//

// Parámetros:

//

// (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.

 

 

                                                                                                         ________
 

 

C.1.4 SRFECHA Servicios de cálculo de fechas y estampillados

 

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

//

// Parámetros:

// (entrada) lFecha: Fecha en formato AAAAMMD

//

// Retorno:

// 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

//

// Parámetros:

// (entrada) lFecha_AMD Fecha en formato AMD

// (salida)  iDD Fragmento DD

// (salida)  iMM Fragmento MM

// (salida)  lAAAA Fragmento AAAA

//

// Retorno:

// 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;

 

 return NO_ERRO;

}

 

 

//---------------------------------------------------------------------------

// Función FDMA

//

// Fragmenta una fecha en formato DMA en sus componentes

//

// Parámetros:

// (entrada) lFecha_DMA Fecha en formato DMA

// (salida)  iDD Fragmento DD

// (salida)  iMM Fragmento MM

// (salida)  lAAAA Fragmento AAAA

//

// Retorno:

// 0 OK

//---------------------------------------------------------------------------

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

 

 

 // Fragmentación de la fecha proporcionada

 

 *iDD = lFecha_DMA / 1000000;

 lDD0 = (*iDD) * 1000000;

 

 lMA = lFecha_DMA - lDD0;

 *iMM = lMA / 10000;

 lMM0 = (*iMM) * 10000;

 

 *lAAAA = lMA - lMM0;

 

 return NO_ERRO;

}

 

 

//---------------------------------------------------------------------------

// Función: ValiFech

//

// Propósito: Valida fecha dada como fragmentos DD MM AAAA

//

// Parámetros:

// (entrada) iDD: Fragmento DD de fecha

//           iMM: Fragmento MM de fecha

//         lAAAA: Fragmento AAAA de fecha

//

// Retorno:

// 0 OK

// 1 Error

//---------------------------------------------------------------------------

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;

 }

 

 return NO_ERRO;

}

                                                                                                         ________

 

 

//---------------------------------------------------------------------------

// Funciones exportables

//---------------------------------------------------------------------------


//---------------------------------------------------------------------------

// Función: SRFECHA_AMD

//

// Propósito: Valida y cambia una fecha de formato DMA a AMD

//

// Parámetros:

// (entrada) lFecha_DMA: Fecha en formato DDMMAAAA

//

// Esta función se utiliza en la calculadora hipotecaria

//

// Retorno:

// Fecha en formato AAAAMMDD ó 0 si error

//---------------------------------------------------------------------------

__declspec(dllexport) long SRFECHA_AMD(long lFecha_DMA)

{

 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

 long lAMD = 0;    // Resultado AAAAMMDD

 

 

 // Fragmentación de la fecha proporcionada

 

 er = FDMA(lFecha_DMA, &iDD, &iMM, &lAAAA);

 if (er) return 0;

 

 

 // Validación

 

 er = ValiFech(iDD, iMM, lAAAA);

 if (er) return 0;

 

 

 // 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

//

// Parámetros:

// (entrada) lFecha_AMD: Fecha en formato AAAAMMDD

//

// Esta función se utiliza en la calculadora hipotecaria

//

// Retorno:

// Fecha en formato DDMMAAAA ó 0 si error

//---------------------------------------------------------------------------

__declspec(dllexport) long SRFECHA_DMA(long lFecha_AMD)

{

 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

 long lDMA = 0;    // Resultado DDMMAAAA

 

 

 // Fragmentación de la fecha proporcionada

 

 er = FAMD(lFecha_AMD, &iDD, &iMM, &lAAAA);

 if (er) return 0;

 

 

 // Validación

 

 er = ValiFech(iDD, iMM, lAAAA);

 if (er) return 0;

 

 

 // Construye fecha de salida

 

 lDMA = iDD * 1000000 + iMM * 10000 + lAAAA;

 

 

 return lDMA;

}

 

 

//---------------------------------------------------------------------------

// Función: SRFECHA_DIFF

//

// Propósito: Obtiene días entre fechas

//

// Esta función se utiliza en la calculadora hipotecaria

//

// Parámetros:

// (entrada) lFede: Fecha desde en formato AAAAMMDD

// (entrada) lFeha: Fecha hasta en formato AAAAMMDD

//

// Retorno:

// Diferencia de días entre fechas

//---------------------------------------------------------------------------

__declspec(dllexport) long SRFECHA_DIFF(long lFede, long lFeha)

{

 long lDiff = 0; // Resultado

 

 lDiff = Dias(lFeha) - Dias(lFede);

 

 return lDiff;

}

 

 

//---------------------------------------------------------------------------

// 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

//

// Parámetros: Ninguno

//

// Retorno: TIME solicitado

//---------------------------------------------------------------------------

__declspec(dllexport) long SRFECHA_TIME(void)

 

* Esta función se presentó en el capítulo dedicado a SRRCW, sección pilas y colas

 

 

 

Para la siguiente función se utiliza la variable global siguiente:

 

 

// Salida de SRFECHA_STAMALFA

 

static char cStamAlfa[] = " ";

 

 

 

como sigue:

 

//---------------------------------------------------------------------------

// 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

//

// Parámetros: Ninguno

//

// Retorno:

// Estampillado solicitado

//---------------------------------------------------------------------------

__declspec(dllexport) char *SRFECHA_STAMALFA(void)

{

 

 // Extrae *Time de la estructura estándar del sistema

 

 struct tm *local;

 time_t tim;

 

 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

//

// Propósito: Devuelve estampillado tipo

//

// "AAAAMMDDHHMMSS" en cStamNum[15]

//

// Parámetros: Ninguno

//

// Retorno:

// Estampillado solicitado

//---------------------------------------------------------------------------

__declspec(dllexport) char *SRFECHA_STAMNUM(void)

 

* Esta función se presentó en el capítulo dedicado a SRRCW, sección pilas y colas

 

 

 

//---------------------------------------------------------------------------

// Función: SRFECHA_STAMNUMINV

//

// Propósito: Devuelve estampillado tipo

//

// "99999999999999 - AAAAMMDDHHMMSS" en cStamNum[15]

//

// Parámetros: Ninguno

//

// Retorno:

// Estampillado inverso solicitado

//---------------------------------------------------------------------------

__declspec(dllexport) char *SRFECHA_STAMNUMINV(void)

 

* Esta función se presentó en el capítulo dedicado a SRRCW, sección pilas y colas

 

                                                                                                         ________

 

 

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"

 

                                                                                                         ________