1 /* we compile as C90 but use snprintf */
2 #define _ISOC99_SOURCE
3 
4 #include "minunit.h"
5 #include <limits.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9 
10 #include "modp_numtoa.h"
11 
12 /* Need only for INFINITY and optionally NAN macros */
13 /* We do not link with -lm */
14 #include <math.h>
15 
testITOA(void)16 static char* testITOA(void)
17 {
18     char buf1[100];
19     char buf2[100];
20     int i;
21     size_t len;
22     for (i = 0; i < 100000; ++i) {
23         sprintf(buf1, "%d", i);
24         len = modp_itoa10(i, buf2);
25         mu_assert_int_equals(len, strlen(buf1));
26         mu_assert_str_equals(buf1, buf2);
27 
28         sprintf(buf1, "%d", -i);
29         len = modp_itoa10(-i, buf2);
30         mu_assert_int_equals(len, strlen(buf1));
31         mu_assert_str_equals(buf1, buf2);
32 
33         sprintf(buf1, "%d", INT_MAX - i);
34         len = modp_itoa10(INT_MAX - i, buf2);
35         mu_assert_int_equals(len, strlen(buf1));
36         mu_assert_str_equals(buf1, buf2);
37 
38         sprintf(buf1, "%d", -(INT_MAX - i));
39         len = modp_itoa10(-(INT_MAX - i), buf2);
40         mu_assert_int_equals(len, strlen(buf1));
41         mu_assert_str_equals(buf1, buf2);
42     }
43     return 0;
44 }
45 
testUITOA(void)46 static char* testUITOA(void)
47 {
48     char buf1[100];
49     char buf2[100];
50     uint32_t i;
51     size_t len;
52     for (i = 0; i < 1000000; ++i) {
53         sprintf(buf1, "%u", i);
54         len = modp_uitoa10(i, buf2);
55         mu_assert_int_equals(len, strlen(buf1));
56         mu_assert_str_equals(buf1, buf2);
57     }
58 
59     for (i = 0; i < 1000000; ++i) {
60         sprintf(buf1, "%u", 0xFFFFFFFFu - i);
61         len = modp_uitoa10(0xFFFFFFFFu - i, buf2);
62         mu_assert_int_equals(len, strlen(buf1));
63         mu_assert_str_equals(buf1, buf2);
64     }
65     return 0;
66 }
67 
testLITOA(void)68 static char* testLITOA(void)
69 {
70     char buf1[100];
71     char buf2[100];
72     long int i;
73     size_t len;
74     for (i = 0; i < 100000; ++i) {
75         sprintf(buf1, "%ld", i);
76         len = modp_litoa10(i, buf2);
77         mu_assert_int_equals(len, strlen(buf1));
78         mu_assert_str_equals(buf1, buf2);
79 
80         sprintf(buf1, "%ld", -i);
81         len = modp_litoa10(-i, buf2);
82         mu_assert_int_equals(len, strlen(buf1));
83         mu_assert_str_equals(buf1, buf2);
84 
85         sprintf(buf1, "%ld", LONG_MAX - i);
86         len = modp_litoa10(LONG_MAX - i, buf2);
87         mu_assert_int_equals(len, strlen(buf1));
88         mu_assert_str_equals(buf1, buf2);
89 
90         sprintf(buf1, "%ld", -(LONG_MAX - i));
91         len = modp_litoa10(-(LONG_MAX - i), buf2);
92         mu_assert_int_equals(len, strlen(buf1));
93         mu_assert_str_equals(buf1, buf2);
94     }
95     return 0;
96 }
97 
testULITOA(void)98 static char* testULITOA(void)
99 {
100     char buf1[100];
101     char buf2[100];
102     size_t len;
103     long long unsigned int i;
104     for (i = 0; i < 1000000; ++i) {
105         sprintf(buf1, "%llu", i);
106         len = modp_ulitoa10(i, buf2);
107         mu_assert_int_equals(len, strlen(buf1));
108         mu_assert_str_equals(buf1, buf2);
109     }
110 
111     for (i = 0; i < 1000000; ++i) {
112         sprintf(buf1, "%llu", 0xFFFFFFFFFFFFFFFFllu - i);
113         len = modp_ulitoa10(0xFFFFFFFFFFFFFFFFull - i, buf2);
114         mu_assert_int_equals(len, strlen(buf1));
115         mu_assert_str_equals(buf1, buf2);
116     }
117     return 0;
118 }
119 
testDoubleToA(void)120 static char* testDoubleToA(void)
121 {
122     char buf1[100];
123     char buf2[100];
124     char msg[200];
125     double d;
126     size_t len;
127     char* tmp;
128     size_t tmplen;
129 
130     /* test each combination of whole number + fraction,
131 	   at every precision */
132     /* and test negative version */
133     double wholes[] = { 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0,
134         67.0, 101.0, 10000, 99999 };
135     double frac[] = { 0.0, 0.1, 0.2, 0.3, 0.4, 0.49, 0.5, 0.51, 0.6, 0.7,
136         0.9, 0.01, 0.25, 0.125, 0.03, 0.0625, 0.0078125,
137         0.001, 0.00001, 0.99, 0.999, 0.9999, 0.99999, 0.999999,
138         0.875, 0.9375, 0.96875, 0.9921875,
139         //		0.95, 0.995, 0.9995, 0.99995, 0.999995, 0.9999995,
140         0.09, 0.099, 0.0999, 0.09999, 0.099999, 0.0999999,
141         0.09999999 };
142 
143     /* TBD
144 	 * 0.95, 0.995, 0.9995, 0.99995, 0.999995, 0.9999995
145 	 * since not exactly represented by floating point
146 	 * printf uses some tricks that we do not use
147 	 * causing test issues
148 	 */
149 
150     const char* formats[] = { "%.0f", "%.1f", "%.2f", "%.3f", "%.4f", "%.5f",
151         "%.6f", "%.7f", "%.8f", "%.9f" };
152 
153     size_t imax = sizeof(wholes) / sizeof(double);
154     size_t jmax = sizeof(frac) / sizeof(double);
155     size_t kmax = sizeof(formats) / sizeof(const char*);
156 
157     size_t i, j, k;
158     for (i = 0; i < imax; ++i) {
159         for (j = 0; j < jmax; ++j) {
160             for (k = 0; k < kmax; ++k) {
161                 d = wholes[i] + frac[j];
162 
163                 sprintf(buf1, formats[k], d);
164                 sprintf(msg, "orig=%f whole=%f, frac=%f, prec=%d -- want %s",
165                     wholes[i] + frac[j], wholes[i], frac[j], (int)k, buf1);
166                 len = modp_dtoa(d, buf2, (int)k);
167                 mu_assert_str_equals_msg(msg, buf1, buf2);
168                 mu_assert_int_equals(len, strlen(buf1));
169 
170                 if ((int)wholes[i] != 0 && (int)(frac[j] * 10000000) != 0) {
171                     sprintf(msg, "whole=%f, frac=%f, prec=%d -- ",
172                         -wholes[i], frac[j], (int)k);
173                     /* not dealing with "-0" issues */
174                     d = -d;
175                     sprintf(buf1, formats[k], d);
176                     len = modp_dtoa(d, buf2, (int)k);
177                     mu_assert_int_equals(len, strlen(buf1));
178                     mu_assert_str_equals_msg(msg, buf1, buf2);
179 
180                     /* find the '.', and see how many chars are after it */
181                     tmp = buf2;
182                     while (*tmp != '.' && *tmp != '\0') {
183                         ++tmp;
184                     }
185                     if (*tmp == '\0') {
186                         tmplen = 0;
187                     } else {
188                         tmplen = strlen(++tmp);
189                     }
190 
191                     sprintf(msg, "whole=%f, frac=%f, prec=%d, got=%d %s-- ",
192                         wholes[i], frac[j], (int)k, (int)tmplen, buf2);
193                     mu_assert_msg(msg, k >= tmplen);
194                 }
195             }
196         }
197     }
198 
199     /* test very large positive number */
200     d = 1.0e200;
201     len = modp_dtoa(d, buf2, 6);
202     mu_assert_int_equals(len, strlen(buf2));
203     mu_assert_str_equals("1.000000e+200", buf2);
204 
205     /* test very large negative number */
206     d = -1.0e200;
207     len = modp_dtoa(d, buf2, 6);
208     mu_assert_int_equals(len, strlen(buf2));
209     mu_assert_str_equals("-1.000000e+200", buf2);
210 
211     /* test very small positive number */
212     d = 1e-10;
213     sprintf(buf1, "%.6f", d);
214     len = modp_dtoa(d, buf2, 6);
215     mu_assert_int_equals(len, strlen(buf1));
216     mu_assert_str_equals(buf1, buf2);
217 
218     /* test very small negative number */
219     d = -1e-10;
220     sprintf(buf1, "%.6f", d);
221     len = modp_dtoa(d, buf2, 6);
222     mu_assert_int_equals(len, strlen(buf1));
223     mu_assert_str_equals(buf1, buf2);
224 
225     return 0;
226 }
227 
228 /* Helper function
229  *  Removes trailing zeros
230  * this is horible but it's just for testing.
231  */
stripTrailingZeros(char * buf)232 static void stripTrailingZeros(char* buf)
233 {
234     size_t i;
235     int hasdot = 0;
236     for (i = 0; i < strlen(buf); ++i) {
237         if (buf[i] == '.') {
238             hasdot = 1;
239             break;
240         }
241     }
242 
243     /* it's just an integer */
244     if (!hasdot) {
245         return;
246     }
247 
248     i = strlen(buf);
249     if (i == 0) {
250         return;
251     }
252     --i;
253 
254     while (i > 0 && (buf[i] == '0' || buf[i] == '.')) {
255         if (buf[i] == '.') {
256             buf[i] = '\0';
257             break;
258         } else {
259             buf[i] = '\0';
260             --i;
261         }
262     }
263 }
264 
testDoubleToA2(void)265 static char* testDoubleToA2(void)
266 {
267     char buf1[100];
268     char buf2[100];
269     char msg[200];
270     double d;
271 
272     char* tmp;
273     size_t len;
274     size_t tmplen;
275 
276     /* test each combination of whole number + fraction,
277 	   at every precision */
278     /* and test negative version */
279     double wholes[] = { 0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0,
280         67.0, 101.0, 10000, 99999 };
281     double frac[] = { 0.0, 0.1, 0.2, 0.3, 0.4, 0.49, 0.5, 0.51, 0.6, 0.7,
282         0.9, 0.01, 0.25, 0.125, 0.03, 0.0625, 0.0078125,
283         0.001, 0.00001, 0.99, 0.999, 0.9999, 0.99999, 0.999999,
284         0.875, 0.9375, 0.96875, 0.9921875,
285         0.09, 0.099, 0.0999, 0.09999, 0.099999, 0.0999999,
286         0.09999999 };
287     const char* formats[] = { "%.0f", "%.1f", "%.2f", "%.3f", "%.4f", "%.5f",
288         "%.6f", "%.7f", "%.8f", "%.9f" };
289 
290     int imax = sizeof(wholes) / sizeof(double);
291     int jmax = sizeof(frac) / sizeof(double);
292     int kmax = sizeof(formats) / sizeof(const char*);
293 
294     int i, j, k;
295     for (i = 0; i < imax; ++i) {
296         for (j = 0; j < jmax; ++j) {
297             for (k = 0; k < kmax; ++k) {
298                 d = wholes[i] + frac[j];
299 
300                 sprintf(msg, "whole=%f, frac=%f, prec=%d -- ",
301                     wholes[i], frac[j], k);
302 
303                 sprintf(buf1, formats[k], d);
304                 stripTrailingZeros(buf1);
305                 len = modp_dtoa2(d, buf2, k);
306 
307                 if ((int)wholes[i] != 0 && (int)(frac[j] * 10000000) != 0) {
308 
309                     /* find the '.', and see how many chars are after it */
310                     tmp = buf2;
311                     while (*tmp != '.' && *tmp != '\0') {
312                         ++tmp;
313                     }
314                     if (*tmp == '\0') {
315                         tmplen = 0;
316                     } else {
317                         tmplen = strlen(++tmp);
318                     }
319 
320                     sprintf(msg, "orig=%f whole=%f, frac=%f, prec=%d -- want %s",
321                         wholes[i] + frac[j], wholes[i], frac[j], (int)k, buf1);
322                     mu_assert_str_equals_msg(msg, buf1, buf2);
323                     mu_assert_msg(msg, (size_t)k >= tmplen);
324 
325                     /* not dealing with "-0" issues */
326                     d = -d;
327                     sprintf(buf1, formats[k], d);
328                     stripTrailingZeros(buf1);
329 
330                     len = modp_dtoa2(d, buf2, k);
331                     mu_assert_int_equals(len, strlen(buf2));
332                     mu_assert_str_equals_msg(msg, buf1, buf2);
333                 }
334             }
335         }
336     }
337 
338     /* test very large positive number */
339     d = 1.0e200;
340     len = modp_dtoa2(d, buf2, 6);
341     mu_assert_int_equals(len, strlen(buf2));
342     mu_assert_str_equals("1.000000e+200", buf2);
343 
344     /* test very large negative number */
345     d = -1.0e200;
346     len = modp_dtoa2(d, buf2, 6);
347     mu_assert_int_equals(len, strlen(buf2));
348     mu_assert_str_equals("-1.000000e+200", buf2);
349 
350     /* test very small positive number */
351     d = 1e-10;
352     sprintf(buf1, "%.6f", d);
353     stripTrailingZeros(buf1);
354 
355     len = modp_dtoa2(d, buf2, 6);
356     mu_assert_int_equals(len, strlen(buf2));
357     mu_assert_str_equals(buf1, buf2);
358 
359     /* test very small negative number */
360     d = -1e-10;
361     sprintf(buf1, "%.6f", d);
362     stripTrailingZeros(buf1);
363 
364     len = modp_dtoa2(d, buf2, 6);
365     mu_assert_int_equals(len, strlen(buf2));
366     mu_assert_str_equals(buf1, buf2);
367 
368     /* test bad precision values */
369     d = 1.1;
370     len = modp_dtoa(d, buf2, -1);
371     mu_assert_int_equals(len, strlen(buf2));
372     mu_assert_str_equals("1", buf2);
373     len = modp_dtoa2(d, buf2, 10);
374     mu_assert_int_equals(len, strlen(buf2));
375     mu_assert_str_equals("1.1", buf2);
376     return 0;
377 }
378 
379 /* From Issue 7  -- http://code.google.com/p/stringencoders/issues/detail?id=7
380  * thanks to http://code.google.com/u/simhasana/
381  */
testOverflowLITOA(void)382 static char* testOverflowLITOA(void)
383 {
384     char buf1[100];
385     char buf2[100];
386 
387     long long int longmin = LONG_MIN;
388     sprintf(buf1, "%lld", longmin);
389     modp_litoa10(longmin, buf2);
390     mu_assert_str_equals(buf1, buf2);
391 
392     long long int longmax = LONG_MAX;
393     sprintf(buf1, "%lld", longmax);
394     modp_litoa10(longmax, buf2);
395     mu_assert_str_equals(buf1, buf2);
396 
397     return 0;
398 }
399 
testOverflowITOA(void)400 static char* testOverflowITOA(void)
401 {
402     char buf1[100];
403     char buf2[100];
404 
405     int32_t intmin = INT_MIN;
406     sprintf(buf1, "%d", intmin);
407     modp_itoa10(intmin, buf2);
408     mu_assert_str_equals(buf1, buf2);
409 
410     int32_t intmax = INT_MAX;
411     sprintf(buf1, "%d", intmax);
412     modp_itoa10(intmax, buf2);
413     mu_assert_str_equals(buf1, buf2);
414 
415     return 0;
416 }
417 
418 /* Test NaN and Infinity behavior */
testDTOANonFinite(void)419 static char* testDTOANonFinite(void)
420 {
421     char buf2[100];
422     double d;
423 
424     /* Test for inf */
425     d = 1e200 * 1e200;
426     /* NOTE!!! next line will core dump!
427 	 * sprintf(buf1, "%.6f", d);
428 	 */
429     buf2[0] = '\0';
430     modp_dtoa2(d, buf2, 6);
431     mu_assert_str_equals("inf", buf2);
432     return 0;
433 }
434 
testDTOAInfinity(void)435 static char* testDTOAInfinity(void)
436 {
437 
438 /* INFINITY should be standard. Defined in <math.h> */
439 /* http://www.gnu.org/s/libc/manual/html_node/Infinity-and-NaN.html */
440 #ifdef INFINITY
441     char buf1[100];
442     char buf2[100];
443     double d = INFINITY;
444 
445     /* test libc support */
446     sprintf(buf1, "%f", d);
447     mu_assert_str_equals("inf", buf1);
448 
449     buf2[0] = '\0';
450     modp_dtoa(d, buf2, 6);
451     mu_assert_str_equals("inf", buf2);
452 
453     buf2[0] = '\0';
454     modp_dtoa2(d, buf2, 6);
455     mu_assert_str_equals("inf", buf2);
456 #endif
457 
458     return 0;
459 }
460 
testDTOAandNAN(void)461 static char* testDTOAandNAN(void)
462 {
463 /* NAN is a GNU extension, defined in <math.h> */
464 /* http://www.gnu.org/s/libc/manual/html_node/Infinity-and-NaN.html */
465 
466 #ifdef NAN
467     char buf1[100];
468     char buf2[100];
469     double d;
470 
471     d = NAN;
472 
473     /* test libc support */
474     sprintf(buf1, "%f", d);
475     mu_assert_str_equals("nan", buf1);
476 
477     /* now test ours */
478     buf2[0] = '\0';
479     modp_dtoa(d, buf2, 6);
480     mu_assert_str_equals("nan", buf2);
481     buf2[0] = '\0';
482     modp_dtoa2(d, buf2, 6);
483     mu_assert_str_equals("nan", buf2);
484 #endif
485 
486     return 0;
487 }
488 
testUITOA16(void)489 static char* testUITOA16(void)
490 {
491     char buf1[100];
492     char buf2[100];
493 
494     modp_uitoa16(1, buf1, 1);
495     mu_assert_str_equals(buf1, "00000001");
496 
497     modp_uitoa16(0, buf1, 1);
498     mu_assert_str_equals(buf1, "00000000");
499 
500     modp_uitoa16(0xFFFFFFFF, buf1, 1);
501     mu_assert_str_equals(buf1, "FFFFFFFF");
502 
503     unsigned int i;
504     for (i = 1; i < 1000000; ++i) {
505         sprintf(buf1, "%08X", i);
506         modp_uitoa16(i, buf2, 1);
507         mu_assert_str_equals(buf1, buf2);
508     }
509     return 0;
510 }
511 
512 /**
513  * Attempt to replicate issue
514  * http://code.google.com/p/stringencoders/issues/detail?id=15
515  */
testRoundingPrecisionOverflow(void)516 static char* testRoundingPrecisionOverflow(void)
517 {
518     char buf1[100];
519 
520     modp_dtoa(0.09999999, buf1, 6);
521     mu_assert_str_equals(buf1, "0.100000");
522     modp_dtoa2(0.09999999, buf1, 6);
523     mu_assert_str_equals(buf1, "0.1");
524     return 0;
525 }
526 
all_tests(void)527 static char* all_tests(void)
528 {
529     mu_run_test(testITOA);
530     mu_run_test(testUITOA);
531     mu_run_test(testLITOA);
532     mu_run_test(testULITOA);
533     mu_run_test(testDoubleToA);
534     mu_run_test(testDoubleToA2);
535     mu_run_test(testOverflowLITOA);
536     mu_run_test(testOverflowITOA);
537     mu_run_test(testDTOANonFinite);
538     mu_run_test(testDTOAInfinity);
539     mu_run_test(testDTOAandNAN);
540     mu_run_test(testUITOA16);
541     mu_run_test(testRoundingPrecisionOverflow);
542     return 0;
543 }
544 
545 UNITTESTS
546