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