aboutsummaryrefslogtreecommitdiff
path: root/flatcc/external/grisu3/grisu3_test.c
blob: 930e02756018bd00acd47ca9eeabb24ca892a6b6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
#include <inttypes.h>
#include <string.h>
#include <stdio.h>

#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;
    }
}