[UCR]  
[/\]
Universidad de Costa Rica
Centro de Investigación en
Matemática Pura y Aplicada
[<=] [home] [<>] [\/] [=>]
Google Translate

insnsep(): Una función más simple para darle formato a cantidades monetarias

insnsep(): A simpler function to format monetary amounts

Adolfo Di Mare




Abstract [<>] [\/] [/\]

Describo la rutina 'insnsep()' que permite insertar a una hilera los separadores que hacen más legible un valor numérico. Para darle formato a hileras C que contienen números es conveniente contar con una rutina que también le ayude al programador a evitar sobreescribir valores cuando los separadores son insertados en sitio, dentro de la hilera a la que se le da formato. I describe 'insnsep()', a routine to insert separators that make a numerical value more readable. To format C strings that contain numbers it is convenient to have a routine that also helps the programmer to avoid overwriting values when the separators are inserted in place, within the string that is formatted.

Introducción [<>] [\/] [/\]

      En este trabajo sugiero una simple solución en C para darle formato a un valor numérico que se encuentra almacenado en una hilera C, que es un vector de caracteres cuyo final está marcado por el cero binario '\0'. C es un lenguaje muy importante que está en la base de la programación, tanto en su historia como en la práctica. En [Kreinin-2012] se argumenta que C puede usarse como el lenguaje objeto que los compiladores producen: "Creo que C es un lenguaje intermedio impresionante para que un compilador emita. Es extremadamente portátil, se compila rápido, se optimiza muy bien y ofrece interoperabilidad con un montón de cosas.". [I think C is an awesome intermediate language for a compiler to emit. It's extremely portable, it compiles in a snap, optimizes nicely, and you get interoperability with loads of stuff.] Aunque uso C++ como mi lenguaje de implementación, si es posible prefiero escribir cada implementación en C.

{
    char num[] = "123456789"; char cpy[ sizeof(num) ];
    memcpy(cpy, num,   strlen(num)); // Falta el EOS
    memcpy(cpy, num, +1strlen(num)); // Ok
    strcpy(cpy, num); // También copia EOS [10 chars]
}
Figura 1

      Debido a que C es un lenguaje "ágil y eficiente" [ lean and mean ] puede ser visto como el común denominador de todos los lenguajes. Pero estas cualidades tienen su costo que asoma su mala cara cuando se usan hileras de caracteres: para almacenar un hilera de 'n' letras, se necesita usar un vector que tenga capacidad para almacenar 'n+1' caracteres. Como el 'EOS==0x0' delimita el final de la hilera, para copiar una hilera 'num', no basta copiar su longitud 'strlen(num)', sino que hay que copiar un caracter adicional, su cero binario final, como se muestra en la Figura 1.

      La ventaja principal de las hileras C es que son tan simples que su uso disminuye el uso de recursos y mejora el rendimiento de programas que manipulan caracteres. Sin embargo, su debilidad más importante es que el programador debe ser "muy cuidadoso" para evitar sobreescribir valores adyacentes a la hilera. Hay muchos ejemplos en Internet de como la deficiencia en el tamaño de las hileras de lenguaje se han sido aprovechadas por "hackers" y programadores maliciosos para perpetrar ataques informáticos [wiki-2019a]. Estos ataques de seguridad logran abrumar a programas que usan hileras relativamente pequeñas y, por eso, el programador C está obligado a verificar constantemente que el tamaño máximo de cada hilera no sea excedido.

{
    char num = "123456789"; char cpy[ 3 ]; int Q=0;
    strcpy(cpy, num); // Copia 10 letras, no solo 3
    assert( Q==0 );   // BOOM: el valor de 'Q' ha cambiado
}
Figura 2

      Como se muestra en la Figura 2, si por error el programador C no ha colocado el EOS en una hilera, las rutinas de biblioteca pueden escribir más allá de la memoria asignada a una variable. En este ejemplo, como la variable 'Q' tiene su valor después de las 3 letras de 'cpy[]', la rutina 'strcpy()' escribe más allá del final de 'cpy[]' y modifica el valor de 'Q': este es un error muy difícil de detectar, que hará que el programa falle posteriormente de una manera muy extraña [weird failure]. En [Seacord-2013] se puede encontrar una discusión con más detalle de las limitaciones de las hileras C.

      Después de hacer cálculos, en muchas aplicaciones hay que darle formato a los valores calculados. El proceso de darle formato a una hilera que representa un valores numéricos o monetarios tiene 3 pasos: lo primero es usar una hilera C para poner el valor numérico en caracteres. El segundo paso es insertarle los separadores en la posición de los miles y el último paso es insertar el signo monetario. Por eso, si la hilera C ya contiene el valor "123456.00", para darle formato hay que agregar el separador ',' en el medio "123,456.00" y luego se pone al principio el signo monetario, para finalmente obtener "$123,467.00".

      En un artículo anterior [DiM-2012] describo la rutina 'mnyfmt()' que permite usar hileras de formato similares a las claúsulas PICTURE de Cobol para darle formato a valores numéricos. Aunque 'mnyfmt()' es una rutina que puede servir en una gran cantidad de aplicaciones, tiene el defecto de que trabaja con valores que deben estar almacenados en variable enteras de tipo '(long)', lo que en algunos casos es incómodo, pues lo usual es almacenar valores monetarios en variables de tipo '(double)' (64 bits) o '(long double)' (96 o 128 bits, 12 o 16 bytes). Otra alternativa es usar la rutina 'strfmon()' que forma parte de la librería GNU C [GNU-2019]. Además, versiones recientes de 'printf()' permiten usar la comilla "'" como modificador para que el valor numérico resultante tenga separadores; en lugar de usar la versión simple "%10d" hay que incluir la comilla '\'' después del indicador de valor "%'10d"; desafortunadamente, se requiere de un compilador que incluya una implementación completa del estándar POSIX 2008 (2013) (hay otras opciones disponibles en la red si uno busca con cuidado, como en [Search-2019]). Muchos programadores, entre los que me incluyo, preferimos usar una rutina más simple y adecuada, y por eso creo que mi rutina 'insnsep()' es una solución válida para resolver el problema de darle formato adecuado a valores numéricos.

      Para combatir las limitaciones de las hileras C, la gente ha implementado rutinas que evitan sobreescribir ["overwrite"] la memoria adyacente a una hilera. Estas son algunas de esas rutinas:

strncpy()
Copia hasta 'n' letras
strlcpy()
Copia hasta 'n' letras y siempre graba EOS==0
strcpy_s()
Otra versión de strncpy()

      En resumen, usar las hileras del lenguaje C es incómodo pues hay que verificar constantemente que no se ha grabado más allá de la capacidad del vector que almacena los caracteres de la hilera. En el contexto de darle formato a una hilera numérica, una estrategia eficaz y simple es incluir de antemano en la hilera a formatear espacio suficiente para insertar los separadores de formato. Esta es la forma en que trabaja 'mnyfmt()' y también 'insnsep()', la rutina descrita en este artículo.


Diseño de 'strdel()' [<>] [\/] [/\]

      Me parece importante describir cómo se desenvuelve el proceso de diseño de una rutina, y para eso uso como ejemplo es la rutina 'strdel()' que elimina una parte de una hilera. Siempre es cierto que al eliminar una parte de la hilera el valor resultante tiene longitud menor (o igual) al valor original, por lo que no hace falta verificar que al borrar se sobreescriban valores adyacentes a la hilera.

char* strdel( // Elimina 'len' letras de 'num' a partir de 'from'
    char  *num;  // hilera a la que se le eliminará una parte
    size_t from; // posición dentro de 'num' adonde comenzar
    size_t len:  // cantidad de characteres a eliminar a partir de 'from'
)
Figura 3

      En la Figura 3 es una especificación razonable para 'strdel()'. Parece necesario incluir estos 3 parámetros para 'strdel()' aunque hay otras posibilidades. En Internet hay varias implementaciones gratuitas de rutinas similares a ésta, pese a que no cumplen exactamente con esta especificación.

// Yang -> C Sample Program for Array and Pointer
/* strdel_yang(num,n,l) function
*  Deleting l characters at position n from num
*     ptr: string pointer
*     n: >=1, 1 means character one
*     l: >=0, 0 means to the end
*/
char* strdel_yang( char *num, int n, int l )
{
    char *p;
    char *b;
    int m = 0;

    p = num;
    while (*p++) m++;    /* get the length, not including \0 */
    if (m1) p++;   /* get to the right position */
    *p = '\0';             /* good for l=0 */

    if (l>0 && n+l<=m) {
        b = p;
        while ((l--)>0) p++; /* skip l characters */
        while ((*b++ = *p++)); /* copy the rest, including \0 */
    }
    return num;
}

http://herongyang.com/Computer-History/
  C-Sample-Program-for-Array-and-Pointer.html
Figura 4

      La implementación de 'strdel()' de la Figura 4 está en la red [Yang-2017], pero diverge ligeramente de lo especificado en la Figura 3.

// Deletes 'len' chars from 'num' starting at index 'from'.
char *strdel(char *num, size_t from, size_t len) {
    size_t slen = strlen(num);
//  if (len==0) { return num; }
    if (from >= slen) { return num; } // out of bounce
    if (from+len > slen) { len = slen-from; }
    memmove( num+(from), num+(from+len), slen-(from+len)+1 );
    return num;
}
Figura 5

      Me dí a la tarea de implementar 'strdel()' en C. Como se muestra en la Figura 5, con un poco de esfuerzo es posible mejorar esa implementación usando las funciones de biblioteca 'strlen()' y 'memmove()' (que traslada bloques de memoria aunque estén superpuestos [Herselman-2016]). Uso un poco de aritmética para calcular la longitud de las partes de la hilera 'num' que hay que desplazar.

{{  // test::strdel()
    char num[] = "12.==.789";
    strdel( num, 2,4 );
    assertTrue( 0==strcmp( num, "12789" ) );

    strdel( num, strlen(num), 1 );
    assertTrue( 0==strcmp( num, "12789" ) );
}}
Figura 6

      En la Figura 6 se muestra un ejemplo de cómo usar 'strdel()'. Es posible usar menos parámetros modificando el funcionamiento de la rutina si siempre se elimina un prefijo de la hilera: esto es lo que hace 'strdel2()'.

{{  // test::strdel2()
    char num[] = "12.==.789";
    strdel2( num+2,4 );
    assertTrue( 0==strcmp( num, "12789" ) );

    strdel2( num+strlen(num), 1 );
    assertTrue( 0==strcmp( num, "12789" ) );
}}
Figura 7

      En la Figura 7 se usa una la rutina modificada 'strdel2()' que elimina 'len' caracteres del principio de la hilera, por lo que no necesita usar el parámetro 'from'.

// Deletes 'len' chars from the start of 'num'.
char *strdel2(char *num, size_t len) {
    size_t slen = strlen(num);
 // if (len==0) { return num; }
    if (len > slen) { len = slen; }
    memmove( num, num+len, slen-len+1 );
    return num;
}
// Implementation copied from strdel() replacing 'from' by 0.
Figura 8

      En la Figura 8 se muestra la implementación de 'strdel2()' obtenida al sustituir por '0' el valor del parámetro 'from' de 'strdel()'. La forma de uso de ambas rutinas es muy similar, pues para llegar a la posición 'from' de la hilera basta sumar ese índice al puntero 'num', como se muestra en la Figura 7.

      La forma de uso de 'strdel()' es similar a la de 'strdel2()' pues basta cambiar una coma ',' por una suma '+':

{
    strdel2( num+2,4 );
    strdel(  num,2,4 );
}

      La ventaja que tiene 'strdel()' sobre 'strdel2()' es que puede detectar errores de invocación cuando la posición a usar está fuera de rango:

{
    strdel2( num+ 3*strlen(num), 4 ); // ERROR!
    strdel(  num, 3*strlen(num), 4 ); // No borra y retorna 'num'
}

      La implementación de 2 argumentos es más simple y, en consecuencia, también es más rápida, aunque posiblemente la diferencia de velocidad entre ambas rutinas sea tan pequeña que es despreciable. O sea que la ventaja de usar 3 argumentos es que es posible verificar que el punto de borrado está dentro de la hilera, lo que es imposible de hacer en el caso de 'strdel2()'. Cuál diseño de las rutinas es mejor? En una biblioteca de programas, lo natural es incluir ambas versiones para que el programador escoja la que prefiera, aunque es recomendable que los programadores novatos solo usen 'strdel()' para disminuir la posibilidad de dejar errores en el programa; en otras palabras, 'strdel()' es más fácil de usar que 'strdel2()'. Este ejemplo ilustra el hecho de que construir una biblioteca de rutinas no es tan simple como parece.

      Para terminar el diseño de 'strdel()' falta definir qué valor retornará la rutina, para lo que hay varias opciones. Primero, simplemente retornar el parámetro 'num' que recibe la rutina: ambas rutinas retornarían valores diferentes en este caso. La segunda opción es retornar un valor de tipo 'bool' que indica si se copiaron o no letras; en este caso la versión de 2 parámetros 'strdel2()' siempre retornará 'true', aún cuando reciba punteros incorrectos. Otra posibilidad es retornar la cantidad de valores trasladados. Para que ambas versiones de 'strdel()' tengan un funcionamiento similar a 'strcpy()', aquí he escogido que siempre el valor retornado sea el parámetro 'num'.

      Si una rutina tiene más parámetros es más difícil de usar que si tiene menos parámetros. Sin embargo, en realidad para un programador es más fácil de usar la versión de 3 parámetros de 'strdel()' pues al usar la otra hay que pensar un poco para avanzar dentro de la hilera a la que se le eliminarán caracteres, pese a que la versión de menos parámetros es más sencilla de implementar.


Diseño de 'insnsep()' [<>] [\/] [/\]

      El primer paso para darle formato a un valor numérico es convertirlo a una hilera. Una forma eficaz de hacerlo es invocar la rutina estándar 'snprintf()' que funciona con enteros y números de punto flotante.

#include <stdio.h> // snprintf(), scanf(), NULL, etc.
#define  eqstr(a,b)  ( 0==strcmp(a,b) )
{{  // test::PRIuMAX()
    char num[ 64 ];
    snprintf( num,64,  "%llu",    UINT64_MAX );
    assertTrue( eqstr( "18446744073709551615", num ) );

    snprintf( num,64,  "%" "lld", INT64_MIN );
    assertTrue( eqstr( "-9223372036854775808", num ) );
}}
#undef eqstr
Figura 9

      En la Figura 9 se usa una hilera resultado grande para obtener el equivalente en caracteres de un número. El modificador 'llu' se usa para el tipo '(long long unsigned)' que tiene 64 bits o más.

      Después de almacenar en una hilera el valor del número, para darle formato hay que insertarle algunos separadores; también es usual incluir no sólo el signo del número sino también un signo de moneda que en el caso del dólar es el signo '$'. Al hacer estas inserciones puedo ocurrir que no haya espacio suficiente para agregarlos; por esa razón es importante que la rutina que inserte separadores permita evitar este tipo de error.

      Se necesitan 16 espacios para almacenar en una hilera C valores numéricos en el rango de los trillones (que son llamados billones en los países hispanos): "$123,456,789,123". En este caso hay 12 dígitos numéricos, 3 separadores ',' y el signo '$'. Si además hay que almacenar 2 decimales, la cantidad de caracteres sube a 19: 2 decimales y el punto '.' separador (y en este caso el tamaño del vector de caracteres debe ser 20 para almacenar el '0x0==EOS'). En C los enteros más grandes, de 64 bits para el tipo '(long long)' pueden tener casi 20 dígitos. Para enteros de 128 bits, esta longitud máxima casi llega a 40 dígitos:

2^16 = 65,536 (5 dígitos)
2^32 = 4,294,967,296 (10 dígitos)
2^64 = 18,446,744,073,709,551,616 (20 dígitos)
2^128 = 340,282,366,920,938,463,463,374,607,431,768,211,456 (39 dígitos)

      Si se usan ((6)) dígitos en la parte fraccional del número, para números de 64 bits hay que usar 36 caracteres [ 2+(20)+(6+1)+((6))+1 ], pues se necesitan almacenar el signo y el símbolo monetario, 20 dígitos, 6 separadores, el punto decimal '.' junto con la parte fraccional de ((6)) dígitos y, finalmente, el cero al final de la hilera EOS. Usando un razonamiento similar se puede concluir que en 64 caracteres cabe un valor de 128 bits junto con sus caracteres de formato. Estos son los tamaños de hilera:

[ 2+(10)+(1+1)+((4))+1 ] → 19
(10) dígitos y ((4)) decimales caben en 20 caracteres
[ 2+(20)+(6+1)+((6))+1 ] → 36
(20) dígitos y ((6)) decimales caben en 36 caracteres
[ 2+(39)+(12+1)+((8))+1 ] → 64
39 dígitos y ((8)) decimales caben en 64 caracteres

      Cabe destacar que el símbolo monetario en ocasiones puede ocupar 2 o más caracteres [wiki-2019b]. Por ejemplo, el símbolo monetario del franco central africano es "FCFA".

      Para evitar contar tantos espacios y números, otra forma de proceder es usar hileras de tamaño 'insnsep_size', o sea, de 96 caracteres. Así al programador le resulta menos tedioso especificar el tamaño adecuado de la hilera pues puede dejar campo al principio de la hilera para insertar los separadores (aunque en realidad esté posponiendo el cálculo para un punto posterior en su programa). En máquinas en que el tamaño del tipo '(char)' es 8 bits '(CHAR_BIT==8)' 96 bytes son 768 bits.

      Es cierto que para valores enteros basta usar una hilera de 64 caracteres, pero agregar 32 bytes a la hilera resultado para llegar a 96 bytes, en la mayor parte de los casos, no tiene mayor consecuencia, pues en un área de trabajo caben valores formateados muy grandes. De todas formas, siempre se puede averiguar la longitud final del valor formateado con solo invocar 'strlen()'. Usar una hilera resultado de un tamaño (ligeramente) superior al mínimo necesario es una buena idea, pues en estos tiempos hay muy pocas ocasiones en que la memoria es tan escasa que se debe ahorrar al máximo.

      Es útil saber que para usar 'snprintf()' sirve usar el encabezado estándar en el que se definen varias hileras para dar formato a valores numéricos. Por ejemplo, la macro PRIuMAX es una hilera que sirve para darle formato al máximo entero sin signo.

#include <stdio.h>    // snprintf(), scanf(), NULL, etc.
#include <inttypes.h> // macro PRIuMAX
#include "strnum.h"   // insnsep()
#define eqstr(a,b) ( 0==strcmp(a,b) )
{{  // test::SepMaxInt()
    char str[ insnsep_size ]; char *first;
    memset(   str,20, ' '); // pone blancos al principio de 'str[]'
    snprintf( str+20,insnsep_size-20, "%" PRIuMAX, UINT64_MAX );
    assertTrue( eqstr( "18446744073709551615", str+20 ) );

    /*=/=*/ first = insnsep( str, ',' , 3 );
    assertTrue( first != NULL );
    assertTrue( eqstr( "18,446,744,073,709,551,615", first ) );
    assertTrue( strlen(first)==2+6*4 && 26==2+6*4 );
}}
#undef eqstr
Figura 10

      En la Figura 10 es un ejemplo de una solución general que usa un vector resultado de tamaño 'insnsep_size==96', en donde se han reservado 20 caracteres para insertar las comas ',' de separación, el signo y el símbolo monetario. Aquí el truco es avanzar 20 espacios hacia adelante al invocar 'snprintf()'.

      Puede parecer un tanto incómodo que el valor numérico esté cargado a la derecha pero, como se nota en la Figura 10, es fácil dejar suficiente espacio al principio de la hilera 'num' simplemente usando un puntero adelantado 'num+20' y se puede argumentar que calcular de antemano cuánto espacio se necesita para insertar los caracteres de separación es un ejercicio que puede mejorar la calidad del programa.

// Inserta separadores 'sep' de formato para el valor numérico 'num' cada 'w' espacios.
// - Retorna un puntero al primer caracter de la hilera formateada.
// - Si no hay espacio suficiente, retorna NULL y no cambia 'num'.
char* insnsep(
    char * num; // valor a formatear
    char   sep; // separador, usualmente es ','
    size_t  w;  // cada 'w' dígitos se inserta el separador
)
Figura 11

      En la Figura 11 está la especificación concisa de 'insnsep()' (la especificación completa y detallada de la rutina está más adelante).

      Es incómodo verificar el valor retornado cada vez que invoca una rutina, pero en el caso de 'insnsep()' es necesario saber tuvo éxito al hacer su trabajo verificando que valor retornado no es nulo 'NULL': el programador se ve obligado a verificar que el valor retornado no sea nulo o se arriesga a que el puntero nulo sea desreferenciado y, en consecuencia, el programa sobreescriba en una parte inválida de la memoria, lo que muchas veces produce un error fatal que termina el programa.

      Como he mostrado al diseñar 'strdel()', es difícil llegar a un diseño que sea completo y adecuado pues ocurre que las primeras versiones de un objeto en programación son más complicadas de lo necesario, lo que también su implementación. A veces no es claro cuál es el trabajo esencial que realmente debe hacer una rutina, pero muchas veces hay que esperar a contar con una implementación concreta para encontrar la especificación adecuada. Por eso, pese que C nació hace más de medio siglo, es hasta ahora que una rutina como 'insnsep()' aparece.

     

Especificación completa de 'insnsep()' [<>] [\/] [/\]

Inserte en su mismo lugar el separador 'sep' para una cadena numérica.

char *insnsep( char* num, char sep, unsigned w )
{{  // test::insnsep()
    char *first, num[insnsep_size];
    strcpy( num,               ".a.=|$+12345678.00" );
    /*=/=*/ first = insnsep( num, ',' , 3 );
    assertTrue( 0==strcmp(first,   "+12,345,678.00" ) );
    assertTrue( 0==strcmp(num, ".a.=+12,345,678.00" ) );
    assertTrue( 4+(3)+(8)+3 == strlen(num) );
    if (first!=num) { --first; *first = '$'; } // '$' sign
    assertTrue( 0==strcmp(first,  "$+12,345,678.00" ) );
    strcpy( num,                  ".|.|-12345.." ); // "$***" Example
    /*=/=*/ first = insnsep( num, ',', 3 );
    assertTrue( 0==strcmp(first,  "-12,345.." ) );
    assertTrue( 0==strcmp(num,    ".|.-12,345.." ) );
    if (first!=num ) { *num = '$';
        while (--first!=num ) { *first='*'; }
    }
    assertTrue( 0==strcmp(first,  "$**-12,345.." ) );
    strcpy( num, " -0" ); // 0 sign
    assertTrue( num+1 == (first=insnsep( num, '?' , 1 )) );
    assertTrue( 0==strcmp(num," -0" ) );
    strcpy( num, " +000000" ); // 0 sign
    assertTrue( num == (first=insnsep( num, ',' , 3 )) );
    assertTrue( 0==strcmp(num,"+000,000" ) );
    assertTrue(  NULL==insnsep( NULL,'|' , 1 ) ); // no string
    strcpy( num, "???.xx" ); // "No Change" examples
    assertTrue(  NULL==insnsep( num, '|' , 1 ) ); // no digits
    strcpy( num, "123.00" );
    assertTrue( NULL==insnsep( num,  ':' , 0 ) ); // 0==w
    assertTrue( NULL==insnsep( num,  '-' , 2 ) ); // No room to insert
    assertTrue( 0==strcmp( num, "123.00" ) );
}}

Insert in place the separator 'sep' for a numeric string.


Más detalles [<>] [\/] [/\]

      Hay 2 casos especiales que es importante mencionar: cómo formatear fechas o la moneda Rupia de la India ["Rupee"]. La forma más simple es escribir una rutina específica que les de formato usando la misma estrategia de 'insnsep()', y por eso conviene contar con las rutinas 'insnsep_Rupee()' y 'insnsep_date8()' (como es lo más conveniente, la rutina para las fechas ingnora el signo '-' o '+').

      Otra estrategia para darle formato a la Rupia, la moneda de la India, es usar 'mnyfmt()' como se muestra en [DiM-2012] en la Figura 13.


Conclusiones [<>] [\/] [/\]

      Es un desafío lograr definir una rutina que sea suficientemente sencilla pero que además sea aplicable en muchas situaciones pues hay muchas variaciones que permiten lograr el mismo resultado. Es importante evitar los errores de sobrescribir memoria. Las rutinas aquí descritas tiene la ventaja de que en muchos casos liberan al programador de la necesidad de estar contando minuciosamente cuánto espacio necesita y, en la práctica, lo que se puede hacer es prever de antemano un tamaño muy grande en caso de que el programador tome la mala decisión de no verificar si el puntero retornado por la rutina es nulo puntero nulo

      En muchos lenguajes modernos se usan excepciones para reportar condiciones de error o eventos anómalos, pero en un lenguaje tan sencillo como C no queda más que retornar un código de error.


Reconocimientos [<>] [\/] [/\]

Eduardo E. Piza Volio:
Investigador y colaborador en el CIMPA
http://cimpa.ucr.ac.cr/index.php/acerca-de/personal/investigadores/eduardo
http://Doxygen.org:
Herramienta de Documentación para C y C++.
http://LanguageTool.org:
Herramienta en línea para corregir texto.

Indice [<>] [\/] [/\]

[1] Introducción
[2] Diseño de 'strdel()'
[3] Diseño de 'insnsep()'
[4] Especificación completa de 'insnsep()'
[5] Más detalles
[6] Conclusiones
[7] Reconocimientos

Bibliografía
Index
Acerca del autor
Acerca de este documento
[/\] Principio [<>] Indice [\/] Fin

Bibliografía [<>] [\/] [/\]


           
[DiM-2012] Di Mare, Adolfo: mnyfmt.c: A simple function to format money or currency amounts, Technical Report 2012-02-ADH, Escuela de Ciencias de la Computación e Informática, Universidad de Costa Rica, 2012.
      http://www.di-mare.com/adolfo/p/mnyfmt.htm
      http://www.di-mare.com/adolfo/p/mnyfmt/mnyfmt.zip
[DiM-2012b] Di Mare, Adolfo: A Simple Function for Formatting Currency, Dr.Dobbs: The World of Software Development, March 26, 2012.
      http://www.drdobbs.com/cpp/232700238
      http://www.drdobbs.com/article/print?articleId=232700238
      http://www.di-mare.com/adolfo/p/mnyfmt/mnyfmt.zip
[GNU-2019] GNU Project: The GNU C Library, visited on 2019.
      http://GNU.org/software/libc/manual
[Herselman-2016] Herselman, T.: Apex memmove - the fastest memcpy/memmove on x86/x64 ... EVER, written in C, Codeproject 2016.
      https://codeproject.com/articles/1110153/apex-memmove-the-fastest-memcpy-memmove-on-x-x-eve
[Kreinin-2012] Kreinin, Yossi: C as an intermediate language, yosefk.com blog, Sep 2012.
      http://yosefk.com/blog/c-as-an-intermediate-language.html
[Seacord-2013] Seacord, Robert C.: Secure Coding in C and C++: Strings and Buffer Overflows, Informit 2013.
      http://informit.com/articles/article.aspx?p=2036582
[Search-2019] Search, Internet: Search for "C lang format money", Google 2019.
      DuckDuckGo → http://ddg.gg/?q=C+lang+format+money+site:stackoverflow.com
      Google → http://google.com/search?num=100&q=C+lang+format+money+site:stackoverflow.com
[wiki-2019a] Wikipedia: Buffer overflow, visited 2019.
      https://en.wikipedia.org/wiki/Buffer_overflow
[wiki-2019b] Wikipedia: Currency symbol, visited 2019.
      https://en.wikipedia.org/wiki/Currency_symbol
[Yang-2017] Yang, Herong: Computer History - Herong's Notes 3.11ed, 2017.
      http://herongyang.com/Computer-History

Index [<>] [\/] [/\]

 

Acerca del autor [<>] [\/] [/\]

Adolfo Di Mare: Investigador costarricense en el Centro de Investigación en Matemática Pura y Aplicada [CIMPA] y en la Escuela de Ciencias de la Computación e Informática [ECCI] de la Universidad de Costa Rica [UCR], en donde ostenta el rango de Profesor Catedrático. Trabaja en las tecnologías de Programación e Internet. También es Catedrático de la Universidad Autónoma de Centro América [UACA]. Obtuvo la Licenciatura en la Universidad de Costa Rica, la Maestría en Ciencias en la Universidad de California, Los Angeles [UCLA], y el Doctorado (Ph.D.) en la Universidad Autónoma de Centro América.
Adolfo Di Mare: Costarrican Researcher at the Centro de Investigación en Matemática Pura y Aplicada [CIMPA] and the Escuela de Ciencias de la Computación e Informática [ECCI], Universidad de Costa Rica [UCR], where he is full professor and works on Internet and programming technologies. He is Cathedraticum at the Universidad Autónoma de Centro América [UACA]. Obtained the Licenciatura at UCR, and the Master of Science in Computer Science from the University of California, Los Angeles [UCLA], and the Ph.D. at the Universidad Autónoma de Centro América.
[mailto]Adolfo Di Mare <adolfo@di-mare.com>

 

Acerca de este documento [<>] [\/] [/\]

Referencia: Di Mare, Adolfo: insnsep(): Una función más simple para darle formato a cantidades monetarias: Technical Report 2019-01-ADH, Centro de Investigación en Matemática Pura y Aplicada [CIMPA], Universidad de Costa Rica, 2019.
Internet: http://www.di-mare.com/adolfo/p/insnsep.htm       Google Translate

http://www.di-mare.com/adolfo/p/src/string_tool.zip → código fuente

http://www.di-mare.com/adolfo/p/string_tool/files.html → Documentación Doxygen
Autor: Adolfo Di Mare <adolfo@di-mare.com>
Contact: Apdo 4249-1000, San José Costa Rica
Tel: (506) 2511-6606       Fax: (506) 2511-4918
Revision: CIMPA, Agosto 2019
Visitantes:

 

Copyright © 2019 Adolfo Di Mare
Derechos de autor reservados © 2019 Adolfo Di Mare <adolfo@di-mare.com>
[home] [<>] [/\]