mnyfmt.c: Format money or currency amounts
mnyfmtts.c
Go to the documentation of this file.
1 // (C) Copyright Adolfo Di Mare 2011
2 // Use, modification and distribution are subject to the
3 // Boost Software License, Version 1.0.
4 // (See accompanying file LICENSE_1_0.txt or copy at
5 // http://www.boost.org/LICENSE_1_0.txt)
6 
7 // Revision history:
8 // 11 Oct 2011 Adolfo Di Mare
9 // Initial (non boost) version
10 
11 // mnyfmtts.c (C) 2011 adolfo@di-mare.com
12 
13 // Test file for mnyfmt()
14 // - Works with Borland C++ 3.1 (1992)
15 // - Works with Code::Blocks 10.05 (2010)
16 
17 /** \file mnyfmtts.c
18  \brief Test program for \c mnyfmt().
19 
20  \author Adolfo Di Mare <adolfo@di-mare.com>
21  \date 2011
22 */
23 
24 // Define this macro if your compiler does not have a (long long) data type
25 // #define MNYFMT_NO_LONG_LONG
26 #include "mnyfmt.h"
27 
28 // This works in the Borland C++ 3.1 IDE
29 // Alt-Options -> Compiler -> Code generation...
30 // -> Defines MNYFMT_NO_LONG_LONG
31 
32 #ifdef __cplusplus
33 // To compile the C source using the C++ compiler, override the file's extension.
34 // The GNU gcc compiler does this with option -x: gcc -x c++
35 // - http://google.com/search?as_qdr=all&num=100&as_q=mix+c+c%2B%2B
36 #endif
37 
38 #include "uUnit.h" // assertTrue()+assertFalse()
39 #include <stdlib.h> // malloc() && free()
40 #include <stdio.h> // printf()
41 #include <string.h> // strcpy() strlen() etc.
42 #include <math.h> // modf()
43 #include <limits.h> // LONG_MAX
44 
45 #if 0
46  #ifndef __cplusplus
47  // namespace polution ???
48  #define mnyfmt( a,b,c,d) mnyfmt(a,b,c,d)
49  #define format_money(a,b,c,d) mnyfmt(a,b,c,d)
50  #else
51  #define mnyfmt( a,b,c,d) mnyfmt(a,b,c,d)
52  #define format_money(a,b,c,d) mnyfmt(a,b,c,d)
53  #endif
54 #endif
55 
56 /// Ensure that, at least for this test program [mnyfmtts.c],
57 /// the formatting \c char is digit \c '9'.
59  [ (mnyfmt_format_char=='9' ? 1 :-1 ) ];
60 
61 /// s1 == s2 ???
62 int eqstr(const char *s1, const char *s2) {
63  #ifndef __cplusplus
64  const int false = 0; const int true = !false;
65  #endif
66  if (s1==0 && s2==0) { return true; }
67  if (s1==0 || s2==0) { return false; }
68  return (strcmp(s1,s2) == 0) ? true : false;
69 }
70 
71 /// Used by 'mnyfmt_wrap()' to check for memory overwrites
72 typedef struct {
73  char *sgn; ///< Returned by mnyfmt()
74  char before [128]; ///< Filler before str[]
75  char str [256]; ///< Format string for mnyfmt()
76  char after [128]; ///< Filler after str[]
77 } FMTSTR;
78 
79 /// Wrapper around 'mnyfmt()' to check that it does not overwrite adyacent memory
80 char* mnyfmt_wrap( char *format, char dec, mnyfmt_long intpart, unsigned CE) {
81  char fill[] = { '!', (char)(0xf0) , 0x0f, 0x8, 2 , 0 };
82  FMTSTR fmtstr[ (sizeof(fill)/sizeof(*fill)) ];
83  unsigned i,dim, len;
84 
85  // handle the null pointer as argument case
86  if (!format) { return mnyfmt( (char*)(0), dec, intpart,CE ); }
87 
88  dim = (sizeof(fill)/sizeof(*fill));
89  len = strlen(format);
90  if (len >= sizeof(fmtstr[0].str) ) { return 0; }
91  for ( i=0; i<dim; ++i ) {
92  memset( fmtstr+i , fill[i] , sizeof(FMTSTR) );
93  strcpy( fmtstr[i].str , format );
94  fmtstr[i].sgn = mnyfmt( fmtstr[i].str, dec, intpart,CE );
95  if ( !fmtstr[i].sgn ) {
96  // check that fmtstr[i].str remains untouched
97  char *p = (char*)(&fmtstr[i]), *pEnd= p+sizeof(fmtstr[0]);
98  while ( p!=pEnd ) {
99  if ( *p != fill[i] ) { return 0; }
100  ++p;
101  }
102  }
103  { // check for overwrites before str[]
104  char *p = fmtstr[i].before;
105  while ( p!= fmtstr[i].str ) {
106  if ( *p != fill[i] ) { return 0; }
107  ++p;
108  }
109  }
110  { // check for overwrites after str[]
111  char *p = fmtstr[i].str+len+1;
112  if ( 0 != *(fmtstr[i].str+len) ) { printf("."); }
113  while ( p!= (char*)(&fmtstr[i])+sizeof(FMTSTR) ) {
114  if ( *p != fill[i] ) { return 0; }
115  ++p;
116  }
117  }
118  }
119  for ( i=1; i<dim; ++i ) {
120  // ensure that all calls returned the same formatted string
121  if ( 0!= strcmp(fmtstr[i-1].str,fmtstr[i].str) ) { return 0; }
122  // ensure that the address of proper significative digit got returned
123  if ( fmtstr[i].sgn != fmtstr[i-1].sgn+sizeof(FMTSTR) ) { return 0; }
124  }
125 
126  // no memory leak problems found: now do the real work
127  return mnyfmt( format, dec, intpart,CE );
128 }
129 
130 /// [Dirty] Trick to force all test cases to use 'mnyfmt_wrap() instead of mnyfmt()'
131 /*
132  This is how it works: after this #define and only within this program file
133  "mnyfmtts.c", the C preprocessor will substitutye the identifier 'mnyfmt' by
134  'mnyfmt_wrap'. When the compiler compiles the resulting preprocessed file,
135  all invocations to funcion 'mnyfmt()' within function 'mnyfmtts()' [with
136  'ts'] would have been replaced by invocations to 'mnyfmt_wrap()', which
137  will test that no memory gets overwritten by the real 'mnyfmt()' function.
138  As this macro gets defined after the body of function 'mnyfmt_wrap()' has
139  been compiled, no errors will be produced.
140 */
141 
142 #define mnyfmt mnyfmt_wrap
143 
144 /// Trick macro to see the value in a string
145 #define show(x) do { printf("%s->[%s]\n", #x, x ); } while(0)
146 /// Abreviation to invoke show(fmtstr)
147 #define sh show(fmtstr);
148 
149 /// test ==> mnyfmt()
150 int mnyfmtts( const int argc , const char* argv[] ) {
151 {{ // test.example
152  // (2^128 < 10^40) && (2*40<96) ==> char[96] holds a
153  char *sgn, fmtstr[96],mem[96]; // 128 bit integer
154 
155  unsigned CE, ten_pow_CE;
156  CE = 2; ten_pow_CE = 100; // 10^CE -> "Currency Exponent"
157 
158  strcpy( mem, "USD$ " ); // Picture clause
159  strcpy( fmtstr , "99,999,999.99999" );
160  if (( sgn = mnyfmt( fmtstr , '.' ,-10245587,CE ) )) {
161  assertTrue( eqstr( fmtstr , "0-,102,455.87000") );
162  if ( (*sgn=='-') && (','==*(sgn+1)) ) { ++sgn; *sgn='-'; }
163  assertTrue( eqstr( sgn, "-102,455.87000") );
164 
165  strcat( mem , sgn );
166  assertTrue( eqstr( mem, "USD$ -102,455.87000") );
167  }
168  else {
169  assertFalse( "ERROR [???]: " "-102,455.87000" );
170  }
171 }}
172 
173 {{ // test.neg.comma
174  // (2^128 < 10^40) && (2*40<96) ==> char[96] holds a
175  char *sgn, fmtstr[96]; // 128 bit integer
176 
177  strcpy( fmtstr , "9,999,999.99" );
178  if (( sgn = mnyfmt( fmtstr , '.' ,-45587,2 ) )) {
179  assertTrue( eqstr( sgn, "-,455.87" ) );
180  assertTrue( eqstr( fmtstr, "0,00-,455.87" ) );
181  // handle the "-," problem
182  if ( (*sgn=='-') && (','==*(sgn+1)) ) {
183  ++sgn; *sgn = '-'; // put '-' sign in a nice place
184  }
185  assertTrue( eqstr( sgn, "-455.87" ) );
186  assertTrue( eqstr( fmtstr, "0,00--455.87" ) );
187  }
188 }}
189 
190 {{ // test.neg.comma.more
191  char *sgn, fmtstr[mnyfmt_size];
192  strcpy( fmtstr , "9,999" );
193  if (( sgn = mnyfmt( fmtstr , '.' ,-2455,0 ) )) {
194  assertFalse( eqstr( sgn, "-2,455" ) );
195  // another '9' is needed to format!
196  // '-' didn't fit on top of a '9'
197  }
198  else {
199  assertTrue( "5 digits are required to format " "-2,455" );
200  assertTrue( sgn == (char*)(0) );
201  }
202 }}
203 
204 {{ // test.-245587
205  char *sgn, fmtstr[mnyfmt_size];
206 
207  strcpy( fmtstr , "999,999,999.99" );
208  if (( sgn = mnyfmt( fmtstr , '.' ,-245587,2 ) )) {
209  assertTrue( eqstr( fmtstr,"000,0-2,455.87" ) );
210  assertTrue( eqstr( sgn, "-2,455.87" ) );
211  // handle the "-," problem
212  if ( (*sgn=='-') && (','==*(sgn+1)) ) {
213  ++sgn; *sgn = '-'; // put '-' sign in a nice place
214  }
215  assertTrue( eqstr( sgn, "-2,455.87" ) );
216  assertTrue( eqstr( fmtstr, "000,0-2,455.87" ) );
217  assertTrue( fmtstr+5 == sgn );
218  }
219 }}
220 
221 {{ // test.PITFALL
222  char *sgn, fmtstr[mnyfmt_size], buffer[mnyfmt_size];
223 
224  strcpy( buffer, "USD$ " ); // Picture clause
225  strcpy( fmtstr , "99,999,999" );
226  if (( sgn = mnyfmt( fmtstr , '.' ,-102455,87 ) )) {
227  assertFalse( "ERROR [CE should be 0 not " ",87" "]" );
228  }
229  else {
230  assertTrue( sgn == 0 ); // NULL means mnyfmt() returns ERROR
231  }
232 
233  strcpy( fmtstr , "99,999,999.9999" );
234  if (( sgn = mnyfmt( fmtstr , '.' ,-1024550077,4 ) )) {
235  assertTrue( eqstr( sgn , "-,102,455.0077" ) );
236  // handle the "-," problem
237  if ( (*sgn=='-') && (','==*(sgn+1)) ) {
238  ++sgn; *sgn = '-'; // put '-' sign in a nice place
239  }
240  assertTrue( eqstr( sgn , "-102,455.0077" ) );
241  }
242 }}
243 
244 {{ // test.few.decimals
245  char *sgn, fmtstr[mnyfmt_size];
246 
247  strcpy( fmtstr , "999,999,999.9999" );
248  if (( sgn = mnyfmt( fmtstr , '.' , -124550077,2 ) )) {
249  assertTrue( eqstr( sgn , "-1,245,500.7700" ) );
250 
251  assertFalse( eqstr( sgn , "-102,455.00" ) );
252  assertFalse( eqstr( sgn , "-102,455.0077" ) );
253  }
254 }}
255 
256 {{ // test.overwrite
257  typedef struct struct_overwrite {
258  long align; // probably aligned
259  char bytes_8[8]; // 64 bits: probably aligned
260  int int_neg; // -1 usually has all its bits equal to 1
261  } overwrite;
262 
263  overwrite o = { 0, {'1','2','3','4','5','6','7','\0' }, -1 };
264  assertTrue( 8-1==strlen(o.bytes_8) && o.int_neg == -1 );
265 
266  strcpy( o.bytes_8, "1234567.." ); // 2 more bytes...
267  assertTrue( 9==strlen(o.bytes_8) );
268  assertFalse( o.int_neg == -1 && "Adjacent memory overwritten " );
269  assertTrue( o.int_neg != -1 );
270 
271  assertTrue( CHAR_BIT == 8 && "8 bits bytes required" );
272 }}
273 
274 {{ // test.sgn.ptr
275  char *sgn, fmtstr[mnyfmt_size]; // Picture clause
276  strcpy( fmtstr , "999.999.999,99" );
277  if (( sgn = mnyfmt( fmtstr , ',', 12345678988,2 ) )) {
278  assertTrue( eqstr( fmtstr , "123.456.789,88" ) );
279  assertTrue( eqstr( fmtstr , sgn ) && (fmtstr == sgn) );
280  }
281  strcpy( fmtstr , "999.999.999,99" );
282  if (( sgn = mnyfmt( fmtstr , ',' , -10245587,2 ) )) {
283  assertTrue( eqstr( fmtstr , "00-.102.455,87" ) );
284  assertTrue( eqstr( sgn , "-.102.455,87" ) );
285  if ( (*sgn=='-') && ('.'==*(sgn+1)) ) { ++sgn; *sgn='-'; }
286  assertTrue( eqstr( sgn , "-102.455,87" ) );
287  }
288  {
289  assertFalse( 0077 == 77 ); // 0077 is an octal literal
290  assertTrue( 0077 == 8*7+7 ); // 0077 == 63 decimal
291  }
292 }}
293 
294 {{ // test.too.small
295  char *sgn, fmtstr[mnyfmt_size];
296  strcpy( fmtstr , "999.99999" );
297  if (( sgn = mnyfmt( fmtstr , '.' , 24558700,4 ) )) {
298  // never executed ==> buffer too small
299  // 2455 has 4>3 digits [999.]
300  }
301  assertTrue( sgn == 0 );
302  assertTrue( eqstr( fmtstr , "999.99999") );
303 }}
304 
305 {{ // test.parentheses
306  char *sgn, fmtstr[mnyfmt_size], buffer[mnyfmt_size];
307 
308  strcpy( buffer, "USD$ " );
309  strcpy( fmtstr , "9,999,999.999" );
310  if (( sgn = mnyfmt( fmtstr , '.' ,-102455087,3 ) )) {
311  if ( *sgn=='-' ) { // put parentheses around the formatted value
312  if (','==*(sgn+1)) { ++sgn; *sgn='-'; } // skip comma
313  strcat( buffer , "(" );
314  strcat( buffer , sgn );
315  strcat( buffer , ")" );
316  assertTrue( eqstr( buffer, "USD$ (-102,455.087)") );
317  }
318  else {
319  strcat( buffer , sgn );
320  }
321  }
322 }}
323 
324 {{
325  typedef char mnyfmt_size_96_is_enough
326  [ (( (65536.0 * 65536.0 * 65536.0 * 65536.0) < 1.0e20 ) ? 1 :-1 ) ];
327  mnyfmt_size_96_is_enough ch;
328  assertTrue( 1 == sizeof(ch) );
329 }}
330 
331 {{ // test.parentheses.no
332  char *sgn, fmtstr[mnyfmt_size], buffer[mnyfmt_size];
333 
334  strcpy( fmtstr , "9,999,999.999" );
335  sgn = mnyfmt( fmtstr , '.' ,2455087,3 );
336  strcpy( buffer, "USD$ " );
337  if ( sgn!=0 ) {
338  if ( *sgn=='-' ) { // put parentheses around formatted value
339  strcat( buffer , "(" );
340  strcat( buffer , sgn );
341  strcat( buffer , ")" );
342  }
343  else {
344  strcat( buffer , sgn );
345  assertTrue( eqstr( buffer, "USD$ 2,455.087") );
346  }
347  }
348 }}
349 
350 {{ // test.asterisks
351  char *sgn, fmtstr[mnyfmt_size];
352 
353  strcpy( fmtstr , "$9,999,999.999" );
354  if (( sgn = mnyfmt( fmtstr , '.' , -455870,3 ) )) {
355  if ( (*sgn=='-') && (','==*(sgn+1)) ) { ++sgn; *sgn='-'; }
356  assertTrue( eqstr( sgn , "-455.870") );
357  for ( --sgn; (sgn!=fmtstr ); --sgn ) {
358  *sgn = '*'; // avoid writing over "$"
359  }
360  assertTrue( eqstr( fmtstr , "$*****-455.870" ) );
361  }
362 }}
363 
364 {{ // test.modf
365  char *sgn, fmtstr[mnyfmt_size];
366 
367  double intdouble, fractdouble;
368  long intpart, fracpart;
369  unsigned CE, ten_pow_CE;
370 
371  CE = 5; ten_pow_CE = 100*1000; // 10^CE
372  fractdouble = modf( 2455.87 , &intdouble );
373  intpart = intdouble; // 2455
374  fracpart = (unsigned)(fractdouble*ten_pow_CE); // .86000
375  {
376  assertFalse( fracpart == 87000 && "???");
377  assertTrue( fracpart == 86999 && "!!!"); // binary rounding...
378  }
379  strcpy( fmtstr , "[[ 999,999.99999 ]]" );
380  if (( sgn = mnyfmt( fmtstr , '.' , intpart*ten_pow_CE+fracpart,CE ) )) {
381  assertTrue( eqstr( fmtstr , "[[ 002,455.86999 ]]" ) );
382  assertTrue( eqstr( sgn , "2,455.86999 ]]") );
383  { // std::round_toward_infinity
384  fracpart = (unsigned)(ceil(fractdouble*100))*1000;
385  strcpy( fmtstr , "[[ 999,999.99999 ]]" );
386  assertTrue( fracpart == 87000 && "!!!");
387  if (( sgn = mnyfmt( fmtstr , '.' , intpart*ten_pow_CE+fracpart,5 ) )) {
388  assertTrue( eqstr( sgn, "2,455.87000 ]]") );
389  }
390  }
391  }
392 }}
393 
394 {{ // test.SCALE
395  #define SCALE(f,CE) ( (mnyfmt_long) ( (f) * 1.0e##CE ) ) // beware of truncation
396 
397  char *sgn, fmtstr[mnyfmt_size];
398  double val_double = -102455.87;
399 
400  strcpy( fmtstr , "99,999,999.99999" );
401  if (( sgn = mnyfmt( fmtstr , '.' ,SCALE(val_double,2),2 ) )) {
402  assertTrue( eqstr( fmtstr , "0-,102,455.86000") );
403  if ( (*sgn=='-') && (','==*(sgn+1)) ) { ++sgn; *sgn='-'; }
404  assertTrue( eqstr( sgn, "-102,455.86000") );
405  } // rounding and trucations yields .86 instead of .87
406  else {
407  assertFalse( "ERROR [???]: " "-102,455.86000" );
408  }
409 }}
410  #undef SCALE
411 
412 {{ // test.0-7.SCALE
413  #define SCALE(f,CE) ( (mnyfmt_long) ( (f) * 1.0e##CE ) )
414  {
415  assertTrue ( SCALE(1,0) == 1 );
416  assertTrue ( SCALE(1,1) == 10 );
417  assertTrue ( SCALE(1,2) == 100 );
418  assertTrue ( SCALE(1,3) == 1000 );
419  assertTrue ( SCALE(1,4) == 10000 );
420  assertTrue ( SCALE(1,5) == 100000 );
421  assertTrue ( SCALE(1,6) == 1000000 );
422  assertTrue ( SCALE(1,7) == 10000000 );
423  }
424  #undef SCALE
425 }}
426 
427 {{ // test.four.decimals
428  char *sgn, fmtstr[mnyfmt_size];
429 
430  double intdouble, fractdouble;
431  long intpart, fracpart;
432  unsigned CE;
433 
434  fractdouble = modf( 2455.87 , &intdouble );
435  intpart = intdouble*10000; // 2455
436  fracpart = (unsigned)(fractdouble*100)*100; // .8600
437  CE = 4;
438  {
439  assertFalse( fracpart == 8700 && "???");
440  assertTrue( fracpart == 8600 && "!!!"); // binary rounding...
441  }
442  strcpy( fmtstr , "[[ 999,999.9999 ]]" );
443  if (( sgn = mnyfmt( fmtstr , '.' , intpart+fracpart,CE ) )) {
444  assertTrue( eqstr( fmtstr , "[[ 002,455.8600 ]]" ) );
445  assertTrue( eqstr( sgn , "2,455.8600 ]]") );
446  { // std::round_toward_infinity
447  fracpart = (unsigned)(ceil(fractdouble*100))*100;
448  strcpy( fmtstr , "[[ 999,999.9999 ]]" );
449  assertTrue( fracpart == 8700 && "!!!");
450  if (( sgn = mnyfmt( fmtstr , '.' , intpart+fracpart,CE ) )) {
451  assertTrue( eqstr( sgn, "2,455.8700 ]]") );
452  }
453  }
454  }
455 }}
456 
457 // This works in the Borland C++ 3.1 IDE
458 // Alt-Options -> Compiler -> Code generation...
459 // -> Defines MNYFMT_NO_LONG_LONG
460 // - Borland C++ 3.1 does not support the (long long) integer type
461 
462 {{ // test.limit
463  char *sgn, fmtstr[mnyfmt_size];
464  #ifndef MNYFMT_NO_LONG_LONG
465  mnyfmt_long max = LONG_LONG_MAX;
466  strcpy( fmtstr , "999,999,999,999,999,999,999" );
467  // 9,223,372,036,854,775,807
468  if ( 9223372036854775807LL == LONG_LONG_MAX ) {
469  if (( sgn = mnyfmt( fmtstr , ' ' , max,0 ) )) {
470  assertTrue( eqstr( "9,223,372,036,854,775,807", sgn ) );
471  }
472  }
473  else {
474  assertFalse( "BEWARE: (long long) is not 8 bytes wide" );
475  }
476  #endif
477  {
478  mnyfmt_long max = LONG_MAX;
479  strcpy( fmtstr , "999,999,999,999" );
480  // 2,147,483,647
481  if ( 2147483647L == LONG_MAX ) {
482  if (( sgn = mnyfmt( fmtstr , ' ' , max,0 ) )) {
483  assertTrue( eqstr( "2,147,483,647", sgn ) );
484  }
485  }
486  else {
487  assertFalse( "BEWARE: (long) is not 4 bytes wide" );
488  }
489  }
490 }}
491 
492 {{ // test.fract.zero
493  char *sgn, fmtstr[mnyfmt_size];
494  // The fraction 643/2136 approximates log10(2) to 7 significant digits.
495  int N = ( ( CHAR_BIT * sizeof(int) - 1 ) * 643 / 2136 ) * (10000*10000);
496 
497  strcpy( fmtstr , "999,999,999,999.999999" );
498  if (( sgn = mnyfmt( fmtstr , '.', -N,0 ) )) {
499  if ( (*sgn=='-') && (','==*(sgn+1)) ) { ++sgn; *sgn='-'; }
500  assertTrue( eqstr( fmtstr ,"00--900,000,000.000000" ) );
501  assertTrue( eqstr( sgn , "-900,000,000.000000" ) );
502 
503  assertFalse( eqstr( fmtstr , "00--455.000000012" ) );
504  assertFalse( eqstr( sgn , "-455.000000012" ) );
505  }
506 }}
507 
508 {{ // test.copy.wchar
509  {{ // Convert a char[] into a wchar_t[]
510  char *src, chBuff[128];
511  wchar_t *dst, *wcBuff;
512  strcpy (chBuff, "Convert me to (wchar_t)");
513  wcBuff = (wchar_t*)( malloc( sizeof(chBuff)*sizeof(wchar_t) ) );
514  for ( dst=wcBuff,src=chBuff; (*dst=*src); ++dst,++src ) {}
515  // ... C++ will let you use more sophisticated stuff
516  free(wcBuff);
517  }}
518  {
519  assertTrue( 1==1 ); // Run to cursor
520  }
521  if ( 0 ) {
522  #if defined(__BORLANDC__) && (__BORLANDC__ <= 0x0410)
523  #define wcslen strlen
524  #endif
525  /// Number of letters in array.
526  #define DIM 256
527  char SRC[DIM], *src;
528  wchar_t DST[DIM], *dst;
529  strcpy( SRC, "Convert me to (wchar_t)");
530  // strlen()==01234567890123456789012345
531  for ( dst=DST,src=SRC; (*dst=*src); ++dst,++src ) {}
532 
533  assertTrue( DIM==sizeof(SRC) );
534  assertTrue( strlen(SRC)==23 );
535 
536  printf( "sizeof(wchar_t) == %d\n", sizeof(wchar_t) );
537  printf("%s strlen(SRC)==%d\n", SRC , strlen(SRC) );
538  printf("%ls wcslen(SRC)==%d\n", DST , wcslen(DST) );
539  #undef wcslen
540  #undef DIM
541  {
542  unsigned i;
543  for ( i=0 ; i<strlen(SRC); ++i ) {
544  assertTrue( DST[i]==(wchar_t)(SRC[i]) );
545  assertTrue( i<strlen(SRC) );
546  }
547  }
548  {
549  wchar_t c1 = (wchar_t)('A');
550  wchar_t c2 = L'A';
551  assertTrue( c1==c2 );
552  printf( "sizeof(wchar_t) == %d\n", sizeof(wchar_t) );
553  }
554  }
555 }}
556 
557 {{ // test.minus.max
558  long long_min = -LONG_MAX-1;
559  assertTrue( long_min<0 );
560  long_min = -long_min;
561  assertTrue( long_min<0 && "?????" );
562  assertTrue( long_min == -long_min );
563 }}
564 
565 {{ // test.stop
566  char *sgn, fmtstr[mnyfmt_size];
567 
568  strcpy( fmtstr , "999,999.9999919,one9." );
569  if (( sgn = mnyfmt( fmtstr , '.', 245587,2 ) )) {
570  if ( (*sgn=='-') && (','==*(sgn+1)) ) { ++sgn; *sgn='-'; }
571  assertTrue( eqstr( fmtstr , "002,455.8700019,one9." ) );
572  assertTrue( eqstr( sgn , "2,455.8700019,one9.") );
573  }
574 }}
575 
576 #ifndef MNYFMT_NO_LONG_LONG
577 {{ // test.rupee
578  char *sgn, fmtstr[mnyfmt_size]; char *p;
579 
580  strcpy( fmtstr , "99,99,99,99,99,99,99,999.99" );
581  // LONG_LONG_MAX == 92,23,37,20,36,85,47,758.07
582  // 3,25,84,729.25
583  // 19 digits: 12 34 56 78 90 12 34 567 89
584 
585  if (( sgn = mnyfmt( fmtstr , '.' , 3258472925LL,2 ) )) {
586  assertTrue( eqstr( sgn , "3,25,84,729.25" ));
587  }
588 
589  strcpy( fmtstr,"99,99,99,99,99 crores 99 lakhs 99,999 rupees.99 paise" );
590  if (( sgn = mnyfmt( fmtstr , '.' , 3258472925LL,2 ) )) {
591  for ( p=sgn; *p!='.'&&*p!=0; ++p ) {} // advance p to dec '.'
592  *p = ' '; // blank the dot: "rupees." ==> "rupees."
593  }
594  assertTrue( eqstr( sgn , "3 crores 25 lakhs 84,729 rupees 25 paise" ));
595 
596  // Rp3,25,84,729.25 is read as three crore(s), twenty-five lakh(s),
597  // eighty-four thousand, seven hundred and twenty-nine rupees and
598  // twenty-five paise.
599  // - http://en.wikipedia.org/wiki/Indian_rupee#Numeral_system
600 }}
601 #endif
602 
603 {{ // test.times
604  char *sgn, fmtstr[mnyfmt_size];
605 
606  strcpy( fmtstr , "99/99/9999" );
607  if (( sgn = mnyfmt( fmtstr , 000, 9272002,0 ) )) {
608  assertTrue( eqstr( fmtstr , "09/27/2002" ) );
609  assertTrue( eqstr( sgn , "9/27/2002" ) );
610  }
611  strcpy( fmtstr , "99:99:99" );
612  if (( sgn = mnyfmt( fmtstr , '?', 21435,0 ))) {
613  assertTrue( eqstr( fmtstr , "02:14:35") );
614  assertTrue( eqstr( sgn , "2:14:35") );
615  }
616 }}
617 
618 {{ // test.times.more
619  char *sgn, fmtstr[mnyfmt_size];
620  strcpy( fmtstr , "99-99-9999" );
621  if (( sgn = mnyfmt( fmtstr , '*' , 07272002,0 ) )) { // 0... octal
622  assertFalse( eqstr( sgn , "07-27-2002" ) );
623  assertTrue( eqstr( sgn , "1-93-0242" ) );
624  assertTrue( eqstr( fmtstr , "01-93-0242" ) );
625  assertTrue( "BEWARE of octal literal !!!" );
626  }
627  strcpy( fmtstr , "99-99-9999" );
628  if (( sgn = mnyfmt( fmtstr , '*' , 27092002,0 ) )) {
629  assertTrue( eqstr( sgn , "27-09-2002" ) );
630  }
631 
632  strcpy( fmtstr , "99-99-9999" );
633  if (( sgn = mnyfmt( fmtstr , '*' , 1012002,0 ) )) {
634  assertTrue( eqstr( sgn , "1-01-2002" ) );
635  assertTrue( eqstr( fmtstr , "01-01-2002" ) );
636  }
637 
638  strcpy( fmtstr , "999,999,999" );
639  if (( sgn = mnyfmt( fmtstr , ':' , 27092002,0 ) )) {
640  assertTrue( eqstr( sgn , "27,092,002" ) );
641  }
642 
643  strcpy( fmtstr , "999,999,999" );
644  if (( sgn = mnyfmt( fmtstr , '.' , 123456789,0 ) )) {
645  assertTrue( eqstr( sgn , "123,456,789" ) );
646  }
647 }}
648 
649 #ifndef MNYFMT_NO_LONG_LONG
650 {{ // test.longlongmax
651  char *sgn, fmtstr[mnyfmt_size];
652 
653  mnyfmt_long max = -LONG_LONG_MAX-1;
654  strcpy( fmtstr , "99,999,999,999,999,999,999" );
655  // 9,223,372,036,854,775,807
656  sgn = mnyfmt( fmtstr , ' ' , max,0 );
657  assertTrue( sgn==0 );
658  assertTrue( eqstr( "99,999,999,999,999,999,999", fmtstr ) );
659 }}
660 #else
661 {{ // test.longmax
662  char *sgn, fmtstr[mnyfmt_size];
663 
664  mnyfmt_long max = -LONG_MAX-1;
665  strcpy( fmtstr , "9,999,999,999" );
666  // 2,147,483,647
667  sgn = mnyfmt( fmtstr , ' ' , max,0 );
668  assertTrue( sgn==0 );
669  assertTrue( eqstr( "9,999,999,999", fmtstr ) );
670  assertTrue( sizeof(mnyfmt_long)==sizeof(long) );
671 }}
672 #endif
673 
674 {{ // test.no.strcpy
675  char *sgn, fmtstr[mnyfmt_size];
676 
677  strcpy( fmtstr , "9,999." );
678  if (( sgn = mnyfmt( fmtstr , '.' , 2455,0 ) )) {
679  assertTrue( eqstr( sgn , "2,455." ) );
680  }
681 
682  if (( sgn = mnyfmt( fmtstr , '.' , 1400,0 ) )) {
683  // never executed: missing strcpy()
684  // no char in "2,455." is a format char
685  }
686  else {
687  assertFalse( eqstr( fmtstr , "1,400." ) );
688  assertTrue( eqstr( fmtstr , "2,455." ) ); // ???
689  assertTrue( "BEWARE: missing strcpy()" ); // ???
690  {
691  strcpy( fmtstr , "9,999." );
692  sgn = mnyfmt( fmtstr , '.' , 1400,0 );
693  assertTrue( eqstr( sgn , "1,400." ) );
694  }
695  }
696 }}
697 
698 {{ // test.dot.comma
699  char *sgn, fmtstr[mnyfmt_size];
700  strcpy( fmtstr , ".9,999." ); // leading dot
701  sgn = mnyfmt( fmtstr , '.' , 2455,0 ); // with dot '.'
702  assertTrue( sgn == 0 && !sgn );
703  assertTrue( eqstr( fmtstr , ".9,999." ) );
704  sgn = mnyfmt( fmtstr , ',' , 2455,0 ); // with comma ','
705  assertTrue( sgn == 0 && !sgn );
706  assertTrue( eqstr( fmtstr , ".9,999." ) );
707  // 2455 has 4 digits ==> it needs '9' format digits before dec.sep
708 
709  strcpy( fmtstr , "9,999" );
710  sgn = mnyfmt( fmtstr , '.' , 2455,0 ); // with dot '.'
711  assertTrue( sgn && sgn != 0 );
712  assertTrue( eqstr( fmtstr , "2,455" ) );
713  sgn = mnyfmt( fmtstr , ',' , 2455,0 ); // with comma ','
714  assertTrue( sgn == 0 && !sgn );
715 
716  strcpy( fmtstr , "9" );
717  sgn = mnyfmt( fmtstr , ',' , 0,0 );
718  assertTrue( eqstr( fmtstr , "0" ) );
719  strcpy( fmtstr , "9" );
720  sgn = mnyfmt( fmtstr , ',' , 9,0 );
721  assertTrue( eqstr( fmtstr , "9" ) );
722 }}
723 
724 {{ // test.zero
725  char *sgn, fmtstr[mnyfmt_size];
726 
727  strcpy( fmtstr , "9.999." );
728  sgn = mnyfmt( fmtstr , '.' ,-0,0 );
729  assertTrue( eqstr( fmtstr , "0.000." ) );
730  assertTrue( eqstr( fmtstr , sgn ) );
731 
732  strcpy( fmtstr , "9," );
733  sgn = mnyfmt( fmtstr , ',' ,-0,0 );
734  assertTrue( eqstr( fmtstr , "0," ) );
735 
736  strcpy( fmtstr , "," );
737  sgn = mnyfmt( fmtstr , ',' ,0,0 );
738  assertTrue( eqstr( fmtstr , "," ) );
739 
740  strcpy( fmtstr , ",9" );
741  sgn = mnyfmt( fmtstr , ',' ,0,0 );
742  assertTrue( eqstr( fmtstr , ",9" ) );
743 
744  strcpy( fmtstr , "9,9" );
745  sgn = mnyfmt( fmtstr , ',' ,0,0 );
746  assertTrue( eqstr( fmtstr , "0,0" ) );
747 
748  strcpy( fmtstr , "9,9999999" );
749  sgn = mnyfmt( fmtstr , ',' ,0,0 );
750  assertTrue( eqstr( fmtstr , "0,0000000" ) );
751 
752  strcpy( fmtstr , "" );
753  sgn = mnyfmt( fmtstr , ',' ,1241,2 );
754  assertTrue( eqstr( fmtstr , "" ) );
755 
756  strcpy( fmtstr , "0123456" );
757  assertTrue( eqstr( fmtstr+1 , "123456" ) );
758  *fmtstr = 0;
759  sgn = mnyfmt( fmtstr , ',' ,1,0 );
760  assertTrue( eqstr( fmtstr , "" ) );
761  assertTrue( eqstr( fmtstr+1 , "123456" ) );
762 }}
763 
764 {{ // test.nines
765  char *sgn, fmtstr[mnyfmt_size];
766  strcpy( fmtstr , "999,99,999" );
767  sgn = mnyfmt( fmtstr , '.' , 99999999,0 );
768  assertTrue( eqstr( sgn , "999,99,999" ) );
769 
770  strcpy( fmtstr , "999,99,999" );
771  sgn = mnyfmt( fmtstr , '.' , 88888888,0 );
772  assertTrue( eqstr( sgn , "888,88,888" ) );
773 }}
774 
775 {{ // test.no.dec
776  char *sgn, fmtstr[mnyfmt_size];
777 
778  strcpy( fmtstr , "9,999." ); // Ok: '.' in "9,999."
779  sgn = mnyfmt( fmtstr , '.' , 2455,0 );
780  assertTrue( eqstr( fmtstr , "2,455." ) );
781 
782  strcpy( fmtstr , "9.999." );
783  sgn = mnyfmt( fmtstr , '?' , 2455,0 ); // NO '?' in "9,999."
784  assertTrue( strchr( fmtstr, '?' ) == 0 );
785  assertTrue( eqstr( fmtstr , "2.455." ) );
786  assertTrue( eqstr( fmtstr , sgn ) );
787 }}
788 
789 {{ // test.Banana
790  char *sgn, fmtstr[mnyfmt_size];
791  // - 24 55 .8
792  strcpy( fmtstr , "Banana 99 x099-099 32 .9.999." );
793  if (( sgn = mnyfmt( fmtstr , '.' ,-245587,2 ) )) {
794  assertTrue( eqstr( "Banana 0- x024-055 32 .8.999.", fmtstr ) );
795  assertTrue( eqstr( "- x024-055 32 .8.999.", sgn ) );
796 
797  assertFalse( eqstr( "Banana 0- x024-055 32 .7.999.", fmtstr ) );
798  assertFalse( eqstr( "- x024-055 32 .7.999.", sgn ) );
799  }
800 }}
801 
802 {{ // test.null.string
803  char *sgn, fmtstr[mnyfmt_size];
804  // - 24 55 .8
805  strcpy( fmtstr , "Banana 99 x099-099 32 .9.999." );
806  strcpy( fmtstr , "" );
807  assertTrue( ! (* fmtstr) );
808  assertTrue( eqstr( fmtstr+1, "anana 99 x099-099 32 .9.999." ) );
809  if (( sgn = mnyfmt( fmtstr , '.' ,-245587,2 ) )) {
810  assertFalse( "NULL string error!!!" );
811  assertFalse( eqstr( "anana 0- x024-055 32 .8.999.", fmtstr+1 ) );
812  }
813  else {
814  assertTrue( "Sorry: I forgot to use a non null string" );
815  }
816 }}
817 
818 {{ // test.null.ptr
819  char *sgn;
820  if (( sgn = mnyfmt( (char*)(0) , '.' ,-245587,2 ) )) {
821  assertFalse( "NULL pointer error!!!" );
822  }
823  else {
824  assertTrue( "Sorry: I forgot to use a non null pointer" );
825  }
826 }}
827 
828 {{ // test.mnyfmt.ptr
829  typedef char* (*format_money_type)( char *, char , mnyfmt_long , unsigned );
830  format_money_type format_money = mnyfmt_wrap;
831  char *sgn, fmtstr[mnyfmt_size];
832  strcpy( fmtstr , "9,999." );
833  if (( sgn = format_money( fmtstr , '.' , 245587,2 ) )) {
834  assertTrue( eqstr( fmtstr , "2,455." ) );
835  assertTrue( eqstr( fmtstr , sgn ) );
836  }
837 }}
838 
839 
840 {{ // test.modf() ==> double modf( double param, double * intpart );
841  { // modf() with positive numbers
842  double fractpart, intpart;
843  fractpart = modf( 3.14159265 , &intpart );
844  assertTrue( intpart == 3.0 );
845  assertTrue( fabs( fractpart - 0.14159265 ) < pow(10.0,-8.0) );
846 
847  if ( fractpart == 0.14159265 ) {
848  assertFalse( "NEVER executed due to floating point rounding error" );
849  // What Every Computer Scientist Should Know About Floating-Point Arithmetic
850  assertTrue( !!! "http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html" );
851  }
852  }
853  { // modf() with negative numbers
854  // NOTE ==> each part has the same sign as param.
855  double fractpart, intpart;
856  fractpart = modf( -3.14159265 , &intpart );
857  assertTrue( intpart == -3.0 ); // intpart is negative
858  assertTrue( fractpart < 0.0 ); // fractpart is negative
859  assertTrue( fabs( -fractpart - 0.14159265 ) < pow(10.0,-8.0) );
860  }
861 }}
862 
863 {{ // test.round
864  double adh_round(double val);
865  /// [Dirty] Trick to force round() to be \c adh_round()
866  #define round adh_round
867  assertTrue( round( 1.7 ) == 2.0 );
868  assertTrue( round( 1.5 ) == 2.0 );
869  assertTrue( round( 1.3 ) == 1.0 );
870 
871  assertTrue( round( -1.3 ) == -1.0 );
872  assertTrue( round( -1.5 ) == -2.0 );
873  assertTrue( round( -1.7 ) == -2.0 );
874 
875  assertTrue( round( 0.1 ) == 0.0 );
876  assertTrue( round( -0.1 ) == 0.0 );
877  assertTrue( round( -0.9 ) == -1.0 );
878 
879  assertTrue( round( 11.7 ) == 12.0 );
880  assertTrue( round( 11.5 ) == 12.0 );
881  assertTrue( round( 11.3 ) == 11.0 );
882 
883  assertTrue( round( -11.3 ) == -11.0 );
884  assertTrue( round( -11.5 ) == -12.0 );
885  assertTrue( round( -11.7 ) == -12.0 );
886  #undef round
887 }}
888  return 0;
889 }
890 
892 {{ // test::07::str2mnyCE()
893  assertTrue( -1024558700 == str2mnyCE(" -102,455.87", '.', 4) );
894  assertTrue( -102455870 == str2mnyCE(" -102,455.87", '.', 3) );
895  assertTrue( -10245587 == str2mnyCE(" -102,455.87", '.', 2) );
896  assertTrue( -1024558 == str2mnyCE(" -102,455.87", '.', 1) );
897  assertTrue( -102455 == str2mnyCE(" -102,455.87", '.', 0) );
898 
899  assertTrue( 1024550700 == str2mnyCE(" 102,455.07", '.', 4) );
900  assertTrue( 102455070 == str2mnyCE(" 102,455.07", '.', 3) );
901  assertTrue( 10245507 == str2mnyCE(" 102,455.07", '.', 2) );
902  assertTrue( 1024550 == str2mnyCE(" 102,455.07", '.', 1) );
903  assertTrue( 102455 == str2mnyCE(" 102,455.07", '.', 0) );
904 }}
905 {{ // test::str2mnyCE()
906  assertTrue( -1024550700 == str2mnyCE(" -102,455.07", '.', 4) );
907  assertTrue( -102455070 == str2mnyCE(" -102,455.07", '.', 3) );
908  assertTrue( -10245507 == str2mnyCE(" -102,455.07", '.', 2) );
909  assertTrue( -1024550 == str2mnyCE(" -102,455.07", '.', 1) );
910  assertTrue( -102455 == str2mnyCE(" -102,455.07", '.', 0) );
911 
912  assertTrue( 1024558700 == str2mnyCE(" 102,455.87", '.', 4) );
913  assertTrue( 102455870 == str2mnyCE(" 102,455.87", '.', 3) );
914  assertTrue( 10245587 == str2mnyCE(" 102,455.87", '.', 2) );
915  assertTrue( 1024558 == str2mnyCE(" 102,455.87", '.', 1) );
916  assertTrue( 102455 == str2mnyCE(" 102,455.87", '.', 0) );
917 }}
918 {
919  assertTrue( -1024558700 == str2mnyCE("$ -102,455.87", '.', 4) );
920  assertTrue( 102455 == str2mnyCE("US$102,455.87", '.', 0) );
921 
922  assertTrue( 2455 == str2mnyCE(" 2,455.87", '.', 0) );
923  assertTrue( 2455 == str2mnyCE(" 2,455.87", '.', 0) );
924  assertTrue( 2455 == str2mnyCE("US$ 2,455 ", '.', 0) );
925  assertTrue( 2455 == str2mnyCE("US$ 2,455. ", '.', 0) );
926 
927  assertTrue( 455000 == str2mnyCE("US$ 455. 1", '.', 3) );
928  assertTrue( 4550000 == str2mnyCE("US$ 455. 2", '.', 4) );
929  assertTrue( 455000 == str2mnyCE("US$ 455", '.', 3) );
930 
931  assertTrue( 4550000 == str2mnyCE("US$ 455.", '.', 4) );
932  assertTrue( 0 == str2mnyCE( ".9",'.', 0) );
933  assertTrue( 9 == str2mnyCE( ".9",'.', 1) );
934  assertTrue( 9 == str2mnyCE(".000009",'.',6) );
935 
936  assertTrue( 4550000 == str2mnyCE("US$ 455", '.', 4) );
937  assertTrue( 455 == str2mnyCE("US$ 455", '.', 0) );
938 
939  assertTrue( 0 == str2mnyCE("BANANA",'.', 0) );
940  assertTrue( 0 == str2mnyCE("BANANA",'.', 4) );
941  assertTrue( 0 == str2mnyCE("BANANA",'.', 6) );
942  assertTrue( 0 == str2mnyCE( "",'.', 6) );
943  assertTrue( 0 == str2mnyCE( "1",'.', 6+1) );
944  assertTrue( 9 == str2mnyCE( "9",'.', 0) );
945 
946  assertTrue( 0 == str2mnyCE( ".9",'.', 0) );
947  assertTrue( 9 == str2mnyCE( ".9",'.', 1) );
948  assertTrue( 9 == str2mnyCE(".000009",'.',6) );
949  assertTrue( 9000000 == str2mnyCE("9",'.',6) );
950  assertTrue( 900000 == str2mnyCE(".9",'.',6) );
951  assertTrue( 0 == str2mnyCE( "-.9",'.', 0) );
952  assertTrue( -9 == str2mnyCE( "-.9",'.', 1) );
953  assertTrue( -9 == str2mnyCE("-.000009",'.',6) );
954 }
955 {
956  assertTrue( !0 ); // 'Run to cursor' in Code::Blocks
957 }
958 }
959 
960 /// Round to integer, rounding halfway cases away from zero
961 double adh_round(double val) { // unbiased rounding
962 // return ( (val>0.0) ? floor(val+0.5) : ceil( val-0.5) );
963  return ( (val>0.0) ? floor(val+0.5) : -floor(-val+0.5) );
964 }
965 
966 /// Main test program
967 int main( const int argc , const char* argv[] ) {
968  printf( "TestCase [mnyfmtts.c]\n" );
969  assertFalse( 1==2 ); // ok!
970  test_str2mnyCE();
971  return mnyfmtts(argc , argv);
972 }
973 
974 // EOF: mnyfmtts.c
#define mnyfmt_size
(2^128 < 10^40) && (2*40 < 96) ==> char[96] is big enough for 128 bits
Definition: mnyfmt.h:23
#define round
char mnyfmtts_REQUIRES_mnyfmt_format_char_EQUAL_TO_digit_9[(mnyfmt_format_char=='9'?1:-1)]
Ensure that, at least for this test program [mnyfmtts.c], the formatting char is digit '9'...
Definition: mnyfmtts.c:59
char * mnyfmt_wrap(char *format, char dec, mnyfmt_long intpart, unsigned CE)
Wrapper around 'mnyfmt()' to check that it does not overwrite adyacent memory.
Definition: mnyfmtts.c:80
char * sgn
Returned by mnyfmt()
Definition: mnyfmtts.c:73
#define assertTrue(cond)
(cond ? () : cout << "cond" )
Definition: uUnit.h:79
#define mnyfmt
[Dirty] Trick to force all test cases to use 'mnyfmt_wrap() instead of mnyfmt()'
Definition: mnyfmtts.c:142
int mnyfmtts(const int argc, const char *argv[])
test ==> mnyfmt()
Definition: mnyfmtts.c:150
#define assertFalse(cond)
(!(cond) ? () : cout << "!" << (cond)" )
Definition: uUnit.h:81
Header file for mnyfmt().
int eqstr(const char *s1, const char *s2)
s1 == s2 ???
Definition: mnyfmtts.c:62
int main(const int argc, const char *argv[])
Main test program.
Definition: mnyfmtts.c:967
#define DIM
void test_str2mnyCE()
Definition: mnyfmtts.c:891
double adh_round(double val)
Round to integer, rounding halfway cases away from zero.
Definition: mnyfmtts.c:961
Used by 'mnyfmt_wrap()' to check for memory overwrites.
Definition: mnyfmtts.c:72
#define SCALE(f, CE)
long long mnyfmt_long
Definition: mnyfmt.h:17
#define mnyfmt_format_char
Formatting character for mnyfmt().
Definition: mnyfmt.h:26
[u]Micro module for [Unit] program testing.
mnyfmt_long str2mnyCE(const char *amount, char dec, unsigned CE)
Returns an integer value that corresponds to 'amount', scaled 10^CE digits.
Definition: str2mnyCE.c:40