#include #include #include #include "grisu3_parse.h" #include "grisu3_print.h" #define TEST(x, s) do { \ if (!(x)) { \ fprintf(stderr, \ "fail: %s\n" \ " input: %s\n" \ " expected: %.17g\n" \ " got: %.17g\n" \ " binary xor: 0x%016"PRId64"\n", \ s, buf, expect, v, (a ^ b)); \ return 1; \ } \ } while (0) static int test_parse_double(char *buf) { const char *k, *end; double v, expect; uint64_t a = 0, b = 0; int len = strlen(buf); end = buf + len; expect = strtod(buf, 0); /* Include '\0' in bytes being parsed to make strtod safe. */ k = grisu3_parse_double(buf, len, &v); /* Make sure we parsed and accepted everything. */ TEST(k == end, "didn't parse to end"); a = grisu3_cast_uint64_from_double(expect); b = grisu3_cast_uint64_from_double(v); #ifdef GRISU3_PARSE_ALLOW_ERROR /* * Just where exponent wraps, this assumption will be incorrect. * TODO: need next higher double function. */ TEST(a - b <= 1, "binary representation differs by more than lsb"); #else /* Binary comparison should match. */ TEST(expect == v, "double representation differs"); TEST(a == b, "binary representation differs"); #endif #if 0 /* This will print the test data also when correct. */ TEST(0, "test case passed, just debugging"); #endif return 0; } /* * We currently do not test grisu3_print_double because * it is a direct port of dtoa_grisu3 from grisu3.c * which presumably has been tested in MathGeoLib. * * grisu3_parse_double is a new implementation. */ int test_suite() { char buf[50]; int fail = 0; fail += test_parse_double("1.23434"); fail += test_parse_double("1234.34"); fail += test_parse_double("1234.34e4"); fail += test_parse_double("1234.34e-4"); fail += test_parse_double("1.23434E+4"); fail += test_parse_double("3.2897984798741413E+194"); fail += test_parse_double("-3.2897984798741413E-194"); sprintf(buf, "3289798479874141.314124124128497098e109"); fail += test_parse_double(buf); sprintf(buf, "3289798479874141.314124124128497098e209"); fail += test_parse_double(buf); sprintf(buf, "-3289798479874141.314124124128497098e209"); fail += test_parse_double(buf); sprintf(buf, "3289798479874141.314124124128497098e+209"); fail += test_parse_double(buf); sprintf(buf, "-3289798479874141.314124124128497098e-209"); fail += test_parse_double(buf); return fail; } void example() { double v; const char *buf = "1234.34e-4"; const char *x, *end; char result_buf[50]; int len; fprintf(stderr, "grisu3_parse_double example:\n parsing '%s' as double\n", buf); /* A non-numeric terminator (e.g. '\0') is required to ensure strtod fallback is safe. */ len = strlen(buf); end = buf + len; x = grisu3_parse_double(buf, len, &v); if (x == 0) { fprintf(stderr, "syntax or range error\n"); } else if (x == buf) { fprintf(stderr, "parse double failed\n"); } else if (x != end) { fprintf(stderr, "parse double did not read everything\n"); } else { fprintf(stderr, "got: %.17g\n", v); } /* * TODO: with the current example: the input "0.123434" is printed * as "1.23434e-1" which is sub-optimal and different from sprintf. * * This is not the grisu3 algorithm but a post formatting step * in grisu3_print_double (originally dtoa_grisu) and may be a bug * in the logic choosing the best print format. * sprintf "%.17g" and "%g" both print as "0.123434" */ fprintf(stderr, "grisu3_print_double example:\n printing %g\n", v); grisu3_print_double(v, result_buf); fprintf(stderr, "got: %s\n", result_buf); } int main() { example(); fprintf(stderr, "running tests\n"); if (test_suite()) { fprintf(stderr, "GRISU3 PARSE TEST FAILED\n"); return -1; } else { fprintf(stderr, "GRISU3 PARSE TEST PASSED\n"); return 0; } }