diff options
Diffstat (limited to 'flatcc/external/grisu3')
-rw-r--r-- | flatcc/external/grisu3/.gitignore | 1 | ||||
-rw-r--r-- | flatcc/external/grisu3/LICENSE | 14 | ||||
-rw-r--r-- | flatcc/external/grisu3/README.md | 9 | ||||
-rw-r--r-- | flatcc/external/grisu3/grisu3_math.h | 329 | ||||
-rw-r--r-- | flatcc/external/grisu3/grisu3_parse.h | 582 | ||||
-rw-r--r-- | flatcc/external/grisu3/grisu3_print.h | 265 | ||||
-rw-r--r-- | flatcc/external/grisu3/grisu3_test.c | 141 | ||||
-rw-r--r-- | flatcc/external/grisu3/grisu3_test_dblcnv.c | 482 | ||||
-rwxr-xr-x | flatcc/external/grisu3/test.sh | 18 | ||||
-rwxr-xr-x | flatcc/external/grisu3/test_dblcnv.sh | 15 |
10 files changed, 1856 insertions, 0 deletions
diff --git a/flatcc/external/grisu3/.gitignore b/flatcc/external/grisu3/.gitignore new file mode 100644 index 0000000..567609b --- /dev/null +++ b/flatcc/external/grisu3/.gitignore @@ -0,0 +1 @@ +build/ diff --git a/flatcc/external/grisu3/LICENSE b/flatcc/external/grisu3/LICENSE new file mode 100644 index 0000000..bb7ca57 --- /dev/null +++ b/flatcc/external/grisu3/LICENSE @@ -0,0 +1,14 @@ +Copyright (c) 2016 Mikkel F. Jørgensen, dvide.com +Some files also Copyright author of MathGeoLib (https://github.com/juj) + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. http://www.apache.org/licenses/LICENSE-2.0 diff --git a/flatcc/external/grisu3/README.md b/flatcc/external/grisu3/README.md new file mode 100644 index 0000000..5f5c62e --- /dev/null +++ b/flatcc/external/grisu3/README.md @@ -0,0 +1,9 @@ +Implements the grisu3 floating point printing and parsing algorithm +based on earlier work: + +- <http://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf> +- <https://github.com/google/double-conversion> +- <https://github.com/juj/MathGeoLib/blob/master/src/Math/grisu3.c> +- <http://www.exploringbinary.com/quick-and-dirty-floating-point-to-decimal-conversion/> + + diff --git a/flatcc/external/grisu3/grisu3_math.h b/flatcc/external/grisu3/grisu3_math.h new file mode 100644 index 0000000..cff6e8c --- /dev/null +++ b/flatcc/external/grisu3/grisu3_math.h @@ -0,0 +1,329 @@ +/* + * Copyright (c) 2016 Mikkel F. Jørgensen, dvide.com + * Copyright author of MathGeoLib (https://github.com/juj) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. http://www.apache.org/licenses/LICENSE-2.0 + */ + +/* 2016-02-02: Updated by mikkelfj + * + * Extracted from MatGeoLib grisu3.c, Apache 2.0 license, and extended. + * + * This file is usually include via grisu3_print.h or grisu3_parse.h. + * + * The original MatGeoLib dtoa_grisu3 implementation is largely + * unchanged except for the uint64 to double cast. The remaining changes + * are file structure, name changes, and new additions for parsing: + * + * - Split into header files only: + * grisu3_math.h, grisu3_print.h, (added grisu3_parse.h) + * + * - names prefixed with grisu3_, grisu3_diy_fp_, GRISU3_. + * - added static to all functions. + * - disabled clang unused function warnings. + * - guarded <stdint.h> to allow for alternative impl. + * - added extra numeric constants needed for parsing. + * - added dec_pow, cast_double_from_diy_fp. + * - changed some function names for consistency. + * - moved printing specific grisu3 functions to grisu3_print.h. + * - changed double to uint64 cast to avoid aliasing. + * - added new grisu3_parse.h for parsing doubles. + * - grisu3_print_double (dtoa_grisu3) format .1 as 0.1 needed for valid JSON output + * and grisu3_parse_double wouldn't consume it. + * - grsu3_print_double changed formatting to prefer 0.012 over 1.2e-2. + * + * These changes make it possible to include the files as headers only + * in other software libraries without risking name conflicts, and to + * extend the implementation with a port of Googles Double Conversion + * strtod functionality for parsing doubles. + * + * Extracted from: rev. 915501a / Dec 22, 2015 + * <https://github.com/juj/MathGeoLib/blob/master/src/Math/grisu3.c> + * MathGeoLib License: http://www.apache.org/licenses/LICENSE-2.0.html + */ + +#ifndef GRISU3_MATH_H +#define GRISU3_MATH_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Guarded to allow inclusion of pstdint.h first, if stdint.h is not supported. */ +#ifndef UINT8_MAX +#include <stdint.h> /* uint64_t etc. */ +#endif + +#ifdef GRISU3_NO_ASSERT +#undef GRISU3_ASSERT +#define GRISU3_ASSERT(x) ((void)0) +#endif + +#ifndef GRISU3_ASSERT +#include <assert.h> /* assert */ +#define GRISU3_ASSERT(x) assert(x) +#endif + +#ifdef _MSC_VER +#pragma warning(disable : 4204) /* nonstandard extension used : non-constant aggregate initializer */ +#endif + +#define GRISU3_D64_SIGN 0x8000000000000000ULL +#define GRISU3_D64_EXP_MASK 0x7FF0000000000000ULL +#define GRISU3_D64_FRACT_MASK 0x000FFFFFFFFFFFFFULL +#define GRISU3_D64_IMPLICIT_ONE 0x0010000000000000ULL +#define GRISU3_D64_EXP_POS 52 +#define GRISU3_D64_EXP_BIAS 1075 +#define GRISU3_D64_DENORM_EXP (-GRISU3_D64_EXP_BIAS + 1) +#define GRISU3_DIY_FP_FRACT_SIZE 64 +#define GRISU3_D_1_LOG2_10 0.30102999566398114 /* 1 / lg(10) */ +#define GRISU3_MIN_TARGET_EXP -60 +#define GRISU3_MASK32 0xFFFFFFFFULL +#define GRISU3_MIN_CACHED_EXP -348 +#define GRISU3_MAX_CACHED_EXP 340 +#define GRISU3_CACHED_EXP_STEP 8 +#define GRISU3_D64_MAX_DEC_EXP 309 +#define GRISU3_D64_MIN_DEC_EXP -324 +#define GRISU3_D64_INF GRISU3_D64_EXP_MASK + +#define GRISU3_MIN(x,y) ((x) <= (y) ? (x) : (y)) +#define GRISU3_MAX(x,y) ((x) >= (y) ? (x) : (y)) + + +typedef struct grisu3_diy_fp +{ + uint64_t f; + int e; +} grisu3_diy_fp_t; + +typedef struct grisu3_diy_fp_power +{ + uint64_t fract; + int16_t b_exp, d_exp; +} grisu3_diy_fp_power_t; + +typedef union { + uint64_t u64; + double d64; +} grisu3_cast_double_t; + +static uint64_t grisu3_cast_uint64_from_double(double d) +{ + grisu3_cast_double_t cd; + cd.d64 = d; + return cd.u64; +} + +static double grisu3_cast_double_from_uint64(uint64_t u) +{ + grisu3_cast_double_t cd; + cd.u64 = u; + return cd.d64; +} + +#define grisu3_double_infinity grisu3_cast_double_from_uint64(GRISU3_D64_INF) +#define grisu3_double_nan grisu3_cast_double_from_uint64(GRISU3_D64_INF + 1) + +static const grisu3_diy_fp_power_t grisu3_diy_fp_pow_cache[] = +{ + { 0xfa8fd5a0081c0288ULL, -1220, -348 }, + { 0xbaaee17fa23ebf76ULL, -1193, -340 }, + { 0x8b16fb203055ac76ULL, -1166, -332 }, + { 0xcf42894a5dce35eaULL, -1140, -324 }, + { 0x9a6bb0aa55653b2dULL, -1113, -316 }, + { 0xe61acf033d1a45dfULL, -1087, -308 }, + { 0xab70fe17c79ac6caULL, -1060, -300 }, + { 0xff77b1fcbebcdc4fULL, -1034, -292 }, + { 0xbe5691ef416bd60cULL, -1007, -284 }, + { 0x8dd01fad907ffc3cULL, -980, -276 }, + { 0xd3515c2831559a83ULL, -954, -268 }, + { 0x9d71ac8fada6c9b5ULL, -927, -260 }, + { 0xea9c227723ee8bcbULL, -901, -252 }, + { 0xaecc49914078536dULL, -874, -244 }, + { 0x823c12795db6ce57ULL, -847, -236 }, + { 0xc21094364dfb5637ULL, -821, -228 }, + { 0x9096ea6f3848984fULL, -794, -220 }, + { 0xd77485cb25823ac7ULL, -768, -212 }, + { 0xa086cfcd97bf97f4ULL, -741, -204 }, + { 0xef340a98172aace5ULL, -715, -196 }, + { 0xb23867fb2a35b28eULL, -688, -188 }, + { 0x84c8d4dfd2c63f3bULL, -661, -180 }, + { 0xc5dd44271ad3cdbaULL, -635, -172 }, + { 0x936b9fcebb25c996ULL, -608, -164 }, + { 0xdbac6c247d62a584ULL, -582, -156 }, + { 0xa3ab66580d5fdaf6ULL, -555, -148 }, + { 0xf3e2f893dec3f126ULL, -529, -140 }, + { 0xb5b5ada8aaff80b8ULL, -502, -132 }, + { 0x87625f056c7c4a8bULL, -475, -124 }, + { 0xc9bcff6034c13053ULL, -449, -116 }, + { 0x964e858c91ba2655ULL, -422, -108 }, + { 0xdff9772470297ebdULL, -396, -100 }, + { 0xa6dfbd9fb8e5b88fULL, -369, -92 }, + { 0xf8a95fcf88747d94ULL, -343, -84 }, + { 0xb94470938fa89bcfULL, -316, -76 }, + { 0x8a08f0f8bf0f156bULL, -289, -68 }, + { 0xcdb02555653131b6ULL, -263, -60 }, + { 0x993fe2c6d07b7facULL, -236, -52 }, + { 0xe45c10c42a2b3b06ULL, -210, -44 }, + { 0xaa242499697392d3ULL, -183, -36 }, + { 0xfd87b5f28300ca0eULL, -157, -28 }, + { 0xbce5086492111aebULL, -130, -20 }, + { 0x8cbccc096f5088ccULL, -103, -12 }, + { 0xd1b71758e219652cULL, -77, -4 }, + { 0x9c40000000000000ULL, -50, 4 }, + { 0xe8d4a51000000000ULL, -24, 12 }, + { 0xad78ebc5ac620000ULL, 3, 20 }, + { 0x813f3978f8940984ULL, 30, 28 }, + { 0xc097ce7bc90715b3ULL, 56, 36 }, + { 0x8f7e32ce7bea5c70ULL, 83, 44 }, + { 0xd5d238a4abe98068ULL, 109, 52 }, + { 0x9f4f2726179a2245ULL, 136, 60 }, + { 0xed63a231d4c4fb27ULL, 162, 68 }, + { 0xb0de65388cc8ada8ULL, 189, 76 }, + { 0x83c7088e1aab65dbULL, 216, 84 }, + { 0xc45d1df942711d9aULL, 242, 92 }, + { 0x924d692ca61be758ULL, 269, 100 }, + { 0xda01ee641a708deaULL, 295, 108 }, + { 0xa26da3999aef774aULL, 322, 116 }, + { 0xf209787bb47d6b85ULL, 348, 124 }, + { 0xb454e4a179dd1877ULL, 375, 132 }, + { 0x865b86925b9bc5c2ULL, 402, 140 }, + { 0xc83553c5c8965d3dULL, 428, 148 }, + { 0x952ab45cfa97a0b3ULL, 455, 156 }, + { 0xde469fbd99a05fe3ULL, 481, 164 }, + { 0xa59bc234db398c25ULL, 508, 172 }, + { 0xf6c69a72a3989f5cULL, 534, 180 }, + { 0xb7dcbf5354e9beceULL, 561, 188 }, + { 0x88fcf317f22241e2ULL, 588, 196 }, + { 0xcc20ce9bd35c78a5ULL, 614, 204 }, + { 0x98165af37b2153dfULL, 641, 212 }, + { 0xe2a0b5dc971f303aULL, 667, 220 }, + { 0xa8d9d1535ce3b396ULL, 694, 228 }, + { 0xfb9b7cd9a4a7443cULL, 720, 236 }, + { 0xbb764c4ca7a44410ULL, 747, 244 }, + { 0x8bab8eefb6409c1aULL, 774, 252 }, + { 0xd01fef10a657842cULL, 800, 260 }, + { 0x9b10a4e5e9913129ULL, 827, 268 }, + { 0xe7109bfba19c0c9dULL, 853, 276 }, + { 0xac2820d9623bf429ULL, 880, 284 }, + { 0x80444b5e7aa7cf85ULL, 907, 292 }, + { 0xbf21e44003acdd2dULL, 933, 300 }, + { 0x8e679c2f5e44ff8fULL, 960, 308 }, + { 0xd433179d9c8cb841ULL, 986, 316 }, + { 0x9e19db92b4e31ba9ULL, 1013, 324 }, + { 0xeb96bf6ebadf77d9ULL, 1039, 332 }, + { 0xaf87023b9bf0ee6bULL, 1066, 340 } +}; + +/* Avoid dependence on lib math to get (int)ceil(v) */ +static int grisu3_iceil(double v) +{ + int k = (int)v; + if (v < 0) return k; + return v - k == 0 ? k : k + 1; +} + +static int grisu3_diy_fp_cached_pow(int exp, grisu3_diy_fp_t *p) +{ + int k = grisu3_iceil((exp+GRISU3_DIY_FP_FRACT_SIZE-1) * GRISU3_D_1_LOG2_10); + int i = (k-GRISU3_MIN_CACHED_EXP-1) / GRISU3_CACHED_EXP_STEP + 1; + p->f = grisu3_diy_fp_pow_cache[i].fract; + p->e = grisu3_diy_fp_pow_cache[i].b_exp; + return grisu3_diy_fp_pow_cache[i].d_exp; +} + +static grisu3_diy_fp_t grisu3_diy_fp_minus(grisu3_diy_fp_t x, grisu3_diy_fp_t y) +{ + grisu3_diy_fp_t d; d.f = x.f - y.f; d.e = x.e; + GRISU3_ASSERT(x.e == y.e && x.f >= y.f); + return d; +} + +static grisu3_diy_fp_t grisu3_diy_fp_multiply(grisu3_diy_fp_t x, grisu3_diy_fp_t y) +{ + uint64_t a, b, c, d, ac, bc, ad, bd, tmp; + grisu3_diy_fp_t r; + a = x.f >> 32; b = x.f & GRISU3_MASK32; + c = y.f >> 32; d = y.f & GRISU3_MASK32; + ac = a*c; bc = b*c; + ad = a*d; bd = b*d; + tmp = (bd >> 32) + (ad & GRISU3_MASK32) + (bc & GRISU3_MASK32); + tmp += 1U << 31; /* round */ + r.f = ac + (ad >> 32) + (bc >> 32) + (tmp >> 32); + r.e = x.e + y.e + 64; + return r; +} + +static grisu3_diy_fp_t grisu3_diy_fp_normalize(grisu3_diy_fp_t n) +{ + GRISU3_ASSERT(n.f != 0); + while(!(n.f & 0xFFC0000000000000ULL)) { n.f <<= 10; n.e -= 10; } + while(!(n.f & GRISU3_D64_SIGN)) { n.f <<= 1; --n.e; } + return n; +} + +static grisu3_diy_fp_t grisu3_cast_diy_fp_from_double(double d) +{ + grisu3_diy_fp_t fp; + uint64_t u64 = grisu3_cast_uint64_from_double(d); + if (!(u64 & GRISU3_D64_EXP_MASK)) { fp.f = u64 & GRISU3_D64_FRACT_MASK; fp.e = 1 - GRISU3_D64_EXP_BIAS; } + else { fp.f = (u64 & GRISU3_D64_FRACT_MASK) + GRISU3_D64_IMPLICIT_ONE; fp.e = (int)((u64 & GRISU3_D64_EXP_MASK) >> GRISU3_D64_EXP_POS) - GRISU3_D64_EXP_BIAS; } + return fp; +} + +static double grisu3_cast_double_from_diy_fp(grisu3_diy_fp_t n) +{ + const uint64_t hidden_bit = GRISU3_D64_IMPLICIT_ONE; + const uint64_t frac_mask = GRISU3_D64_FRACT_MASK; + const int denorm_exp = GRISU3_D64_DENORM_EXP; + const int exp_bias = GRISU3_D64_EXP_BIAS; + const int exp_pos = GRISU3_D64_EXP_POS; + + grisu3_diy_fp_t v = n; + uint64_t e_biased; + + while (v.f > hidden_bit + frac_mask) { + v.f >>= 1; + ++v.e; + } + if (v.e < denorm_exp) { + return 0.0; + } + while (v.e > denorm_exp && (v.f & hidden_bit) == 0) { + v.f <<= 1; + --v.e; + } + if (v.e == denorm_exp && (v.f & hidden_bit) == 0) { + e_biased = 0; + } else { + e_biased = (uint64_t)(v.e + exp_bias); + } + return grisu3_cast_double_from_uint64((v.f & frac_mask) | (e_biased << exp_pos)); +} + +/* pow10_cache[i] = 10^(i-1) */ +static const unsigned int grisu3_pow10_cache[] = { 0, 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; + +static int grisu3_largest_pow10(uint32_t n, int n_bits, uint32_t *power) +{ + int guess = ((n_bits + 1) * 1233 >> 12) + 1/*skip first entry*/; + if (n < grisu3_pow10_cache[guess]) --guess; /* We don't have any guarantees that 2^n_bits <= n. */ + *power = grisu3_pow10_cache[guess]; + return guess; +} + +#ifdef __cplusplus +} +#endif + +#endif /* GRISU3_MATH_H */ diff --git a/flatcc/external/grisu3/grisu3_parse.h b/flatcc/external/grisu3/grisu3_parse.h new file mode 100644 index 0000000..3d67c9a --- /dev/null +++ b/flatcc/external/grisu3/grisu3_parse.h @@ -0,0 +1,582 @@ +/* + * Copyright (c) 2016 Mikkel F. Jørgensen, dvide.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. http://www.apache.org/licenses/LICENSE-2.0 + */ + +/* + * Port of parts of Google Double Conversion strtod functionality + * but with fallback to strtod instead of a bignum implementation. + * + * Based on grisu3 math from MathGeoLib. + * + * See also grisu3_math.h comments. + */ + +#ifndef GRISU3_PARSE_H +#define GRISU3_PARSE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef UINT8_MAX +#include <stdint.h> +#endif + +#include <stdlib.h> +#include <limits.h> + +#include "grisu3_math.h" + + +/* + * The maximum number characters a valid number may contain. The parse + * fails if the input length is longer but the character after max len + * was part of the number. + * + * The length should not be set too high because it protects against + * overflow in the exponent part derived from the input length. + */ +#define GRISU3_NUM_MAX_LEN 1000 + +/* + * The lightweight "portable" C library recognizes grisu3 support if + * included first. + */ +#define grisu3_parse_double_is_defined 1 + +/* + * Disable to compare performance and to test diy_fp algorithm in + * broader range. + */ +#define GRISU3_PARSE_FAST_CASE + +/* May result in a one off error, otherwise when uncertain, fall back to strtod. */ +//#define GRISU3_PARSE_ALLOW_ERROR + + +/* + * The dec output exponent jumps in 8, so the result is offset at most + * by 7 when the input is within range. + */ +static int grisu3_diy_fp_cached_dec_pow(int d_exp, grisu3_diy_fp_t *p) +{ + const int cached_offset = -GRISU3_MIN_CACHED_EXP; + const int d_exp_dist = GRISU3_CACHED_EXP_STEP; + int i, a_exp; + + GRISU3_ASSERT(GRISU3_MIN_CACHED_EXP <= d_exp); + GRISU3_ASSERT(d_exp < GRISU3_MAX_CACHED_EXP + d_exp_dist); + + i = (d_exp + cached_offset) / d_exp_dist; + a_exp = grisu3_diy_fp_pow_cache[i].d_exp; + p->f = grisu3_diy_fp_pow_cache[i].fract; + p->e = grisu3_diy_fp_pow_cache[i].b_exp; + + GRISU3_ASSERT(a_exp <= d_exp); + GRISU3_ASSERT(d_exp < a_exp + d_exp_dist); + + return a_exp; +} + +/* + * Ported from google double conversion strtod using + * MathGeoLibs diy_fp functions for grisu3 in C. + * + * ulp_half_error is set if needed to trunacted non-zero trialing + * characters. + * + * The actual value we need to encode is: + * + * (sign ? -1 : 1) * fraction * 2 ^ (exponent - fraction_exp) + * where exponent is the base 10 exponent assuming the decimal point is + * after the first digit. fraction_exp is the base 10 magnitude of the + * fraction or number of significant digits - 1. + * + * If the exponent is between 0 and 22 and the fraction is encoded in + * the lower 53 bits (the largest bit is implicit in a double, but not + * in this fraction), then the value can be trivially converted to + * double without loss of precision. If the fraction was in fact + * multiplied by trailing zeroes that we didn't convert to exponent, + * we there are larger values the 53 bits that can also be encoded + * trivially - but then it is better to handle this during parsing + * if it is worthwhile. We do not optimize for this here, because it + * can be done in a simple check before calling, and because it might + * not be worthwile to do at all since it cery likely will fail for + * numbers printed to be convertible back to double without loss. + * + * Returns 0 if conversion was not exact. In that case the vale is + * either one smaller than the correct one, or the correct one. + * + * Exponents must be range protected before calling otherwise cached + * powers will blow up. + * + * Google Double Conversion seems to prefer the following notion: + * + * x >= 10^309 => +Inf + * x <= 10^-324 => 0, + * + * max double: HUGE_VAL = 1.7976931348623157 * 10^308 + * min double: 4.9406564584124654 * 10^-324 + * + * Values just below or above min/max representable number + * may round towards large/small non-Inf/non-neg values. + * + * but `strtod` seems to return +/-HUGE_VAL on overflow? + */ +static int grisu3_diy_fp_encode_double(uint64_t fraction, int exponent, int fraction_exp, int ulp_half_error, double *result) +{ + /* + * Error is measures in fractions of integers, so we scale up to get + * some resolution to represent error expressions. + */ + const int log2_error_one = 3; + const int error_one = 1 << log2_error_one; + const int denorm_exp = GRISU3_D64_DENORM_EXP; + const uint64_t hidden_bit = GRISU3_D64_IMPLICIT_ONE; + const int diy_size = GRISU3_DIY_FP_FRACT_SIZE; + const int max_digits = 19; + + int error = ulp_half_error ? error_one / 2 : 0; + int d_exp = (exponent - fraction_exp); + int a_exp; + int o_exp; + grisu3_diy_fp_t v = { fraction, 0 }; + grisu3_diy_fp_t cp; + grisu3_diy_fp_t rounded; + int mag; + int prec; + int prec_bits; + int half_way; + + /* When fractions in a double aren't stored with implicit msb fraction bit. */ + + /* Shift fraction to msb. */ + v = grisu3_diy_fp_normalize(v); + /* The half point error moves up while the exponent moves down. */ + error <<= -v.e; + + a_exp = grisu3_diy_fp_cached_dec_pow(d_exp, &cp); + + /* Interpolate between cached powers at distance 8. */ + if (a_exp != d_exp) { + int adj_exp = d_exp - a_exp - 1; + static grisu3_diy_fp_t cp_10_lut[] = { + { 0xa000000000000000ULL, -60 }, + { 0xc800000000000000ULL, -57 }, + { 0xfa00000000000000ULL, -54 }, + { 0x9c40000000000000ULL, -50 }, + { 0xc350000000000000ULL, -47 }, + { 0xf424000000000000ULL, -44 }, + { 0x9896800000000000ULL, -40 }, + }; + GRISU3_ASSERT(adj_exp >= 0 && adj_exp < 7); + v = grisu3_diy_fp_multiply(v, cp_10_lut[adj_exp]); + + /* 20 decimal digits won't always fit in 64 bit. + * (`fraction_exp` is one less than significant decimal + * digits in fraction, e.g. 1 * 10e0). + * If we cannot fit, introduce 1/2 ulp error + * (says double conversion reference impl.) */ + if (1 + fraction_exp + adj_exp > max_digits) { + error += error_one / 2; + } + } + + v = grisu3_diy_fp_multiply(v, cp); + /* + * Google double conversion claims that: + * + * The error introduced by a multiplication of a*b equals + * error_a + error_b + error_a*error_b/2^64 + 0.5 + * Substituting a with 'input' and b with 'cached_power' we have + * error_b = 0.5 (all cached powers have an error of less than 0.5 ulp), + * error_ab = 0 or 1 / error_oner > error_a*error_b/ 2^64 + * + * which in our encoding becomes: + * error_a = error_one/2 + * error_ab = 1 / error_one (rounds up to 1 if error != 0, or 0 * otherwise) + * fixed_error = error_one/2 + * + * error += error_a + fixed_error + (error ? 1 : 0) + * + * (this isn't entirely clear, but that is as close as we get). + */ + error += error_one + (error ? 1 : 0); + + o_exp = v.e; + v = grisu3_diy_fp_normalize(v); + /* Again, if we shift the significant bits, the error moves along. */ + error <<= o_exp - v.e; + + /* + * The value `v` is bounded by 2^mag which is 64 + v.e. because we + * just normalized it by shifting towards msb. + */ + mag = diy_size + v.e; + + /* The effective magnitude of the IEEE double representation. */ + mag = mag >= diy_size + denorm_exp ? diy_size : mag <= denorm_exp ? 0 : mag - denorm_exp; + prec = diy_size - mag; + if (prec + log2_error_one >= diy_size) { + int e_scale = prec + log2_error_one - diy_size - 1; + v.f >>= e_scale; + v.e += e_scale; + error = (error >> e_scale) + 1 + error_one; + prec -= e_scale; + } + rounded.f = v.f >> prec; + rounded.e = v.e + prec; + prec_bits = (int)(v.f & ((uint64_t)1 << (prec - 1))) * error_one; + half_way = (int)((uint64_t)1 << (prec - 1)) * error_one; + if (prec >= half_way + error) { + rounded.f++; + /* Prevent overflow. */ + if (rounded.f & (hidden_bit << 1)) { + rounded.f >>= 1; + rounded.e += 1; + } + } + *result = grisu3_cast_double_from_diy_fp(rounded); + return half_way - error >= prec_bits || prec_bits >= half_way + error; +} + +/* + * `end` is unchanged if number is handled natively, or it is the result + * of strtod parsing in case of fallback. + */ +static const char *grisu3_encode_double(const char *buf, const char *end, int sign, uint64_t fraction, int exponent, int fraction_exp, int ulp_half_error, double *result) +{ + const int max_d_exp = GRISU3_D64_MAX_DEC_EXP; + const int min_d_exp = GRISU3_D64_MIN_DEC_EXP; + + char *v_end; + + /* Both for user experience, and to protect internal power table lookups. */ + if (fraction == 0 || exponent < min_d_exp) { + *result = 0.0; + goto done; + } + if (exponent - 1 > max_d_exp) { + *result = grisu3_double_infinity; + goto done; + } + + /* + * `exponent` is the normalized value, fraction_exp is the size of + * the representation in the `fraction value`, or one less than + * number of significant digits. + * + * If the final value can be kept in 53 bits and we can avoid + * division, then we can convert to double quite fast. + * + * ulf_half_error only happens when fraction is maxed out, so + * fraction_exp > 22 by definition. + * + * fraction_exp >= 0 always. + * + * http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/ + */ + + +#ifdef GRISU3_PARSE_FAST_CASE + if (fraction < (1ULL << 53) && exponent >= 0 && exponent <= 22) { + double v = (double)fraction; + /* Multiplying by 1e-k instead of dividing by 1ek results in rounding error. */ + switch (exponent - fraction_exp) { + case -22: v /= 1e22; break; + case -21: v /= 1e21; break; + case -20: v /= 1e20; break; + case -19: v /= 1e19; break; + case -18: v /= 1e18; break; + case -17: v /= 1e17; break; + case -16: v /= 1e16; break; + case -15: v /= 1e15; break; + case -14: v /= 1e14; break; + case -13: v /= 1e13; break; + case -12: v /= 1e12; break; + case -11: v /= 1e11; break; + case -10: v /= 1e10; break; + case -9: v /= 1e9; break; + case -8: v /= 1e8; break; + case -7: v /= 1e7; break; + case -6: v /= 1e6; break; + case -5: v /= 1e5; break; + case -4: v /= 1e4; break; + case -3: v /= 1e3; break; + case -2: v /= 1e2; break; + case -1: v /= 1e1; break; + case 0: break; + case 1: v *= 1e1; break; + case 2: v *= 1e2; break; + case 3: v *= 1e3; break; + case 4: v *= 1e4; break; + case 5: v *= 1e5; break; + case 6: v *= 1e6; break; + case 7: v *= 1e7; break; + case 8: v *= 1e8; break; + case 9: v *= 1e9; break; + case 10: v *= 1e10; break; + case 11: v *= 1e11; break; + case 12: v *= 1e12; break; + case 13: v *= 1e13; break; + case 14: v *= 1e14; break; + case 15: v *= 1e15; break; + case 16: v *= 1e16; break; + case 17: v *= 1e17; break; + case 18: v *= 1e18; break; + case 19: v *= 1e19; break; + case 20: v *= 1e20; break; + case 21: v *= 1e21; break; + case 22: v *= 1e22; break; + } + *result = v; + goto done; + } +#endif + + if (grisu3_diy_fp_encode_double(fraction, exponent, fraction_exp, ulp_half_error, result)) { + goto done; + } +#ifdef GRISU3_PARSE_ALLOW_ERROR + goto done; +#endif + *result = strtod(buf, &v_end); + if (v_end < end) { + return v_end; + } + return end; +done: + if (sign) { + *result = -*result; + } + return end; +} + +/* + * Returns buf if number wasn't matched, or null if number starts ok + * but contains invalid content. + */ +static const char *grisu3_parse_hex_fp(const char *buf, const char *end, int sign, double *result) +{ + (void)buf; + (void)end; + (void)sign; + *result = 0.0; + /* Not currently supported. */ + return buf; +} + +/* + * Returns end pointer on success, or null, or buf if start is not a number. + * Sets result to 0.0 on error. + * Reads up to len + 1 bytes from buffer where len + 1 must not be a + * valid part of a number, but all of buf, buf + len need not be a + * number. Leading whitespace is NOT valid. + * Very small numbers are truncated to +/-0.0 and numerically very large + * numbers are returns as +/-infinity. + * + * A value must not end or begin with '.' (like JSON), but can have + * leading zeroes (unlike JSON). A single leading zero followed by + * an encoding symbol may or may not be interpreted as a non-decimal + * encoding prefix, e.g. 0x, but a leading zero followed by a digit is + * NOT interpreted as octal. + * A single leading negative sign may appear before digits, but positive + * sign is not allowed and space after the sign is not allowed. + * At most the first 1000 characters of the input is considered. + */ +static const char *grisu3_parse_double(const char *buf, size_t len, double *result) +{ + const char *mark, *k, *end; + int sign = 0, esign = 0; + uint64_t fraction = 0; + int exponent = 0; + int ee = 0; + int fraction_exp = 0; + int ulp_half_error = 0; + + *result = 0.0; + + end = buf + len + 1; + + /* Failsafe for exponent overflow. */ + if (len > GRISU3_NUM_MAX_LEN) { + end = buf + GRISU3_NUM_MAX_LEN + 1; + } + + if (buf == end) { + return buf; + } + mark = buf; + if (*buf == '-') { + ++buf; + sign = 1; + if (buf == end) { + return 0; + } + } + if (*buf == '0') { + ++buf; + /* | 0x20 is lower case ASCII. */ + if (buf != end && (*buf | 0x20) == 'x') { + k = grisu3_parse_hex_fp(buf, end, sign, result); + if (k == buf) { + return mark; + } + return k; + } + /* Not worthwhile, except for getting the scale of integer part. */ + while (buf != end && *buf == '0') { + ++buf; + } + } else { + if (*buf < '1' || *buf > '9') { + /* + * If we didn't see a sign, just don't recognize it as + * number, otherwise make it an error. + */ + return sign ? 0 : mark; + } + fraction = (uint64_t)(*buf++ - '0'); + } + k = buf; + /* + * We do not catch trailing zeroes when there is no decimal point. + * This misses an opportunity for moving the exponent down into the + * fast case. But it is unlikely to be worthwhile as it complicates + * parsing. + */ + while (buf != end && *buf >= '0' && *buf <= '9') { + if (fraction >= UINT64_MAX / 10) { + fraction += *buf >= '5'; + ulp_half_error = 1; + break; + } + fraction = fraction * 10 + (uint64_t)(*buf++ - '0'); + } + fraction_exp = (int)(buf - k); + /* Skip surplus digits. Trailing zero does not introduce error. */ + while (buf != end && *buf == '0') { + ++exponent; + ++buf; + } + if (buf != end && *buf >= '1' && *buf <= '9') { + ulp_half_error = 1; + ++exponent; + ++buf; + while (buf != end && *buf >= '0' && *buf <= '9') { + ++exponent; + ++buf; + } + } + if (buf != end && *buf == '.') { + ++buf; + k = buf; + if (*buf < '0' || *buf > '9') { + /* We don't accept numbers without leading or trailing digit. */ + return 0; + } + while (buf != end && *buf >= '0' && *buf <= '9') { + if (fraction >= UINT64_MAX / 10) { + if (!ulp_half_error) { + fraction += *buf >= '5'; + ulp_half_error = 1; + } + break; + } + fraction = fraction * 10 + (uint64_t)(*buf++ - '0'); + --exponent; + } + fraction_exp += (int)(buf - k); + while (buf != end && *buf == '0') { + ++exponent; + ++buf; + } + if (buf != end && *buf >= '1' && *buf <= '9') { + ulp_half_error = 1; + ++buf; + while (buf != end && *buf >= '0' && *buf <= '9') { + ++buf; + } + } + } + /* + * Normalized exponent e.g: 1.23434e3 with fraction = 123434, + * fraction_exp = 5, exponent = 3. + * So value = fraction * 10^(exponent - fraction_exp) + */ + exponent += fraction_exp; + if (buf != end && (*buf | 0x20) == 'e') { + if (end - buf < 2) { + return 0; + } + ++buf; + if (*buf == '+') { + ++buf; + if (buf == end) { + return 0; + } + } else if (*buf == '-') { + esign = 1; + ++buf; + if (buf == end) { + return 0; + } + } + if (*buf < '0' || *buf > '9') { + return 0; + } + ee = *buf++ - '0'; + while (buf != end && *buf >= '0' && *buf <= '9') { + /* + * This test impacts performance and we do not need an + * exact value just one large enough to dominate the fraction_exp. + * Subsequent handling maps large absolute ee to 0 or infinity. + */ + if (ee <= 0x7fff) { + ee = ee * 10 + *buf - '0'; + } + ++buf; + } + } + exponent = exponent + (esign ? -ee : ee); + + /* + * Exponent is now a base 10 normalized exponent so the absolute value + * is less the 10^(exponent + 1) for positive exponents. For + * denormalized doubles (using 11 bit exponent 0 with a fraction + * shiftet down, extra small numbers can be achieved. + * + * https://en.wikipedia.org/wiki/Double-precision_floating-point_format + * + * 10^-324 holds the smallest normalized exponent (but not value) and + * 10^308 holds the largest exponent. Internally our lookup table is + * only safe to use within a range slightly larger than this. + * Externally, a slightly larger/smaller value represents NaNs which + * are technically also possible to store as a number. + * + */ + + /* This also protects strod fallback parsing. */ + if (buf == end) { + return 0; + } + return grisu3_encode_double(mark, buf, sign, fraction, exponent, fraction_exp, ulp_half_error, result); +} + +#ifdef __cplusplus +} +#endif + +#endif /* GRISU3_PARSE_H */ diff --git a/flatcc/external/grisu3/grisu3_print.h b/flatcc/external/grisu3/grisu3_print.h new file mode 100644 index 0000000..d748408 --- /dev/null +++ b/flatcc/external/grisu3/grisu3_print.h @@ -0,0 +1,265 @@ +/* + * Copyright (c) 2016 Mikkel F. Jørgensen, dvide.com + * Copyright author of MathGeoLib (https://github.com/juj) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. http://www.apache.org/licenses/LICENSE-2.0 + */ + +/* + * Extracted from MathGeoLib. + * + * mikkelfj: + * - Fixed final output when printing single digit negative exponent to + * have leading zero (important for JSON). + * - Changed formatting to prefer 0.012 over 1.2-e-2. + * + * Large portions of the original grisu3.c file has been moved to + * grisu3_math.h, the rest is placed here. + * + * See also comments in grisu3_math.h. + * + * MatGeoLib grisu3.c comment: + * + * This file is part of an implementation of the "grisu3" double to string + * conversion algorithm described in the research paper + * + * "Printing Floating-Point Numbers Quickly And Accurately with Integers" + * by Florian Loitsch, available at + * http://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf + */ + +#ifndef GRISU3_PRINT_H +#define GRISU3_PRINT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdio.h> /* sprintf, only needed for fallback printing */ +#include <assert.h> /* assert */ + +#include "grisu3_math.h" + +/* + * The lightweight "portable" C library recognizes grisu3 support if + * included first. + */ +#define grisu3_print_double_is_defined 1 + +/* + * Not sure we have an exact definition, but we get up to 23 + * emperically. There is some math ensuring it does not go awol though, + * like 18 digits + exponent or so. + * This max should be safe size buffer for printing, including zero term. + */ +#define GRISU3_PRINT_MAX 30 + +static int grisu3_round_weed(char *buffer, int len, uint64_t wp_W, uint64_t delta, uint64_t rest, uint64_t ten_kappa, uint64_t ulp) +{ + uint64_t wp_Wup = wp_W - ulp; + uint64_t wp_Wdown = wp_W + ulp; + while(rest < wp_Wup && delta - rest >= ten_kappa + && (rest + ten_kappa < wp_Wup || wp_Wup - rest >= rest + ten_kappa - wp_Wup)) + { + --buffer[len-1]; + rest += ten_kappa; + } + if (rest < wp_Wdown && delta - rest >= ten_kappa + && (rest + ten_kappa < wp_Wdown || wp_Wdown - rest > rest + ten_kappa - wp_Wdown)) + return 0; + + return 2*ulp <= rest && rest <= delta - 4*ulp; +} + +static int grisu3_digit_gen(grisu3_diy_fp_t low, grisu3_diy_fp_t w, grisu3_diy_fp_t high, char *buffer, int *length, int *kappa) +{ + uint64_t unit = 1; + grisu3_diy_fp_t too_low = { low.f - unit, low.e }; + grisu3_diy_fp_t too_high = { high.f + unit, high.e }; + grisu3_diy_fp_t unsafe_interval = grisu3_diy_fp_minus(too_high, too_low); + grisu3_diy_fp_t one = { 1ULL << -w.e, w.e }; + uint32_t p1 = (uint32_t)(too_high.f >> -one.e); + uint64_t p2 = too_high.f & (one.f - 1); + uint32_t div; + *kappa = grisu3_largest_pow10(p1, GRISU3_DIY_FP_FRACT_SIZE + one.e, &div); + *length = 0; + + while(*kappa > 0) + { + uint64_t rest; + char digit = (char)(p1 / div); + buffer[*length] = '0' + digit; + ++*length; + p1 %= div; + --*kappa; + rest = ((uint64_t)p1 << -one.e) + p2; + if (rest < unsafe_interval.f) return grisu3_round_weed(buffer, *length, grisu3_diy_fp_minus(too_high, w).f, unsafe_interval.f, rest, (uint64_t)div << -one.e, unit); + div /= 10; + } + + for(;;) + { + char digit; + p2 *= 10; + unit *= 10; + unsafe_interval.f *= 10; + /* Integer division by one. */ + digit = (char)(p2 >> -one.e); + buffer[*length] = '0' + digit; + ++*length; + p2 &= one.f - 1; /* Modulo by one. */ + --*kappa; + if (p2 < unsafe_interval.f) return grisu3_round_weed(buffer, *length, grisu3_diy_fp_minus(too_high, w).f * unit, unsafe_interval.f, p2, one.f, unit); + } +} + +static int grisu3(double v, char *buffer, int *length, int *d_exp) +{ + int mk, kappa, success; + grisu3_diy_fp_t dfp = grisu3_cast_diy_fp_from_double(v); + grisu3_diy_fp_t w = grisu3_diy_fp_normalize(dfp); + + /* normalize boundaries */ + grisu3_diy_fp_t t = { (dfp.f << 1) + 1, dfp.e - 1 }; + grisu3_diy_fp_t b_plus = grisu3_diy_fp_normalize(t); + grisu3_diy_fp_t b_minus; + grisu3_diy_fp_t c_mk; /* Cached power of ten: 10^-k */ + uint64_t u64 = grisu3_cast_uint64_from_double(v); + assert(v > 0 && v <= 1.7976931348623157e308); /* Grisu only handles strictly positive finite numbers. */ + if (!(u64 & GRISU3_D64_FRACT_MASK) && (u64 & GRISU3_D64_EXP_MASK) != 0) { b_minus.f = (dfp.f << 2) - 1; b_minus.e = dfp.e - 2;} /* lower boundary is closer? */ + else { b_minus.f = (dfp.f << 1) - 1; b_minus.e = dfp.e - 1; } + b_minus.f = b_minus.f << (b_minus.e - b_plus.e); + b_minus.e = b_plus.e; + + mk = grisu3_diy_fp_cached_pow(GRISU3_MIN_TARGET_EXP - GRISU3_DIY_FP_FRACT_SIZE - w.e, &c_mk); + + w = grisu3_diy_fp_multiply(w, c_mk); + b_minus = grisu3_diy_fp_multiply(b_minus, c_mk); + b_plus = grisu3_diy_fp_multiply(b_plus, c_mk); + + success = grisu3_digit_gen(b_minus, w, b_plus, buffer, length, &kappa); + *d_exp = kappa - mk; + return success; +} + +static int grisu3_i_to_str(int val, char *str) +{ + int len, i; + char *s; + char *begin = str; + if (val < 0) { *str++ = '-'; val = -val; } + s = str; + + for(;;) + { + int ni = val / 10; + int digit = val - ni*10; + *s++ = (char)('0' + digit); + if (ni == 0) + break; + val = ni; + } + *s = '\0'; + len = (int)(s - str); + for(i = 0; i < len/2; ++i) + { + char ch = str[i]; + str[i] = str[len-1-i]; + str[len-1-i] = ch; + } + + return (int)(s - begin); +} + +static int grisu3_print_nan(uint64_t v, char *dst) +{ + static char hexdigits[16] = "0123456789ABCDEF"; + int i = 0; + + dst[0] = 'N'; + dst[1] = 'a'; + dst[2] = 'N'; + dst[3] = '('; + dst[20] = ')'; + dst[21] = '\0'; + dst += 4; + for (i = 15; i >= 0; --i) { + dst[i] = hexdigits[v & 0x0F]; + v >>= 4; + } + return 21; +} + +static int grisu3_print_double(double v, char *dst) +{ + int d_exp, len, success, decimals, i; + uint64_t u64 = grisu3_cast_uint64_from_double(v); + char *s2 = dst; + assert(dst); + + /* Prehandle NaNs */ + if ((u64 << 1) > 0xFFE0000000000000ULL) return grisu3_print_nan(u64, dst); + /* Prehandle negative values. */ + if ((u64 & GRISU3_D64_SIGN) != 0) { *s2++ = '-'; v = -v; u64 ^= GRISU3_D64_SIGN; } + /* Prehandle zero. */ + if (!u64) { *s2++ = '0'; *s2 = '\0'; return (int)(s2 - dst); } + /* Prehandle infinity. */ + if (u64 == GRISU3_D64_EXP_MASK) { *s2++ = 'i'; *s2++ = 'n'; *s2++ = 'f'; *s2 = '\0'; return (int)(s2 - dst); } + + success = grisu3(v, s2, &len, &d_exp); + /* If grisu3 was not able to convert the number to a string, then use old sprintf (suboptimal). */ + if (!success) return sprintf(s2, "%.17g", v) + (int)(s2 - dst); + + /* We now have an integer string of form "151324135" and a base-10 exponent for that number. */ + /* Next, decide the best presentation for that string by whether to use a decimal point, or the scientific exponent notation 'e'. */ + /* We don't pick the absolute shortest representation, but pick a balance between readability and shortness, e.g. */ + /* 1.545056189557677e-308 could be represented in a shorter form */ + /* 1545056189557677e-323 but that would be somewhat unreadable. */ + decimals = GRISU3_MIN(-d_exp, GRISU3_MAX(1, len-1)); + + /* mikkelfj: + * fix zero prefix .1 => 0.1, important for JSON export. + * prefer unscientific notation at same length: + * -1.2345e-4 over -1.00012345, + * -1.0012345 over -1.2345e-3 + */ + if (d_exp < 0 && (len + d_exp) > -3 && len <= -d_exp) + { + /* mikkelfj: fix zero prefix .1 => 0.1, and short exponents 1.3e-2 => 0.013. */ + memmove(s2 + 2 - d_exp - len, s2, (size_t)len); + s2[0] = '0'; + s2[1] = '.'; + for (i = 2; i < 2-d_exp-len; ++i) s2[i] = '0'; + len += i; + } + else if (d_exp < 0 && len > 1) /* Add decimal point? */ + { + for(i = 0; i < decimals; ++i) s2[len-i] = s2[len-i-1]; + s2[len++ - decimals] = '.'; + d_exp += decimals; + /* Need scientific notation as well? */ + if (d_exp != 0) { s2[len++] = 'e'; len += grisu3_i_to_str(d_exp, s2+len); } + } + /* Add scientific notation? */ + else if (d_exp < 0 || d_exp > 2) { s2[len++] = 'e'; len += grisu3_i_to_str(d_exp, s2+len); } + /* Add zeroes instead of scientific notation? */ + else if (d_exp > 0) { while(d_exp-- > 0) s2[len++] = '0'; } + s2[len] = '\0'; /* grisu3 doesn't null terminate, so ensure termination. */ + return (int)(s2+len-dst); +} + +#ifdef __cplusplus +} +#endif + +#endif /* GRISU3_PRINT_H */ diff --git a/flatcc/external/grisu3/grisu3_test.c b/flatcc/external/grisu3/grisu3_test.c new file mode 100644 index 0000000..930e027 --- /dev/null +++ b/flatcc/external/grisu3/grisu3_test.c @@ -0,0 +1,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; + } +} diff --git a/flatcc/external/grisu3/grisu3_test_dblcnv.c b/flatcc/external/grisu3/grisu3_test_dblcnv.c new file mode 100644 index 0000000..f0e98cc --- /dev/null +++ b/flatcc/external/grisu3/grisu3_test_dblcnv.c @@ -0,0 +1,482 @@ +/* + * Test cases from Googles Double Conversion Library + * + * https://github.com/google/double-conversion/blob/master/test/cctest/test-strtod.cc + * + * Added extra tests for grisu parse print roundtrip and negative sign. + */ + +#include <string.h> +#include <stdio.h> +#include <math.h> + +#include "grisu3_print.h" +#include "grisu3_parse.h" + +#define BEGIN_TEST(name) int test_ ## name() { \ + int fail = 0; char *id = #name; double v; char *vector; \ + char buf[1001]; + +#define END_TEST() return fail; } + + +void check_double(double x1, double x2, char *id, int line, int *fail) +{ + char tmp[50]; + const char *k; + int n; + int failed = 0; + double v; + + if (x1 != x2) { + failed = 1; + fprintf(stderr, "%d: fail (%s): %.17g != %.17g\n", + line, id, x1, x2); + } else { +#if 1 + n = grisu3_print_double(x1, tmp); + if (n >= GRISU3_PRINT_MAX) { /* Leave space for zterm. */ + failed = 1; + fprintf(stderr, "%d: fail (%s): print length exceeded max: %d, input: %.17g\n", + line, id, n, x1); + } else if ((int)strlen(tmp) != n) { + failed = 1; + fprintf(stderr, "%d: fail (%s): print length does not match strlen of output, input: %.17g, got: %s\n", + line, id, x1, tmp); + } else if (!isinf(x1)) { + /* We do expect print/parse to handle inf. */ + k = grisu3_parse_double(tmp, n, &v); + if (k == 0 || k == tmp) { + failed = 1; + fprintf(stderr, "%d: fail (%s): roundtrip parse failed " + "input: %g, printed value %s\n", + line, id, x1, tmp); + } else if (x1 != v) { + failed = 1; + fprintf(stderr, "%d: fail (%s): print/parse roundtrip mismatch for " + "input: %.17g, got %.17g\n", + line, id, x1, v); + } + } +#endif + } + *fail += failed; +} + +#define CHECK_EQ(v1, v2) check_double((v1), (v2), id, __LINE__, &fail) + +#define StringToVector(f) f + +#define Strtod(f, e) (sprintf(buf, "%se%d", f, e), \ + grisu3_parse_double(buf, strlen(buf), &v), v) + +#define StrtodChar(f, e) (sprintf(buf, "%se%d", f, e), \ + grisu3_parse_double(buf, strlen(buf), &v), v) + +#define double_infinity grisu3_double_infinity + +BEGIN_TEST(Strtod) + vector = StringToVector("0"); + CHECK_EQ(0.0, Strtod(vector, 1)); + CHECK_EQ(0.0, Strtod(vector, 2)); + CHECK_EQ(0.0, Strtod(vector, -2)); + CHECK_EQ(0.0, Strtod(vector, -999)); + CHECK_EQ(0.0, Strtod(vector, +999)); + + vector = StringToVector("1"); + CHECK_EQ(1.0, Strtod(vector, 0)); + CHECK_EQ(10.0, Strtod(vector, 1)); + CHECK_EQ(100.0, Strtod(vector, 2)); + CHECK_EQ(1e20, Strtod(vector, 20)); + CHECK_EQ(1e22, Strtod(vector, 22)); + CHECK_EQ(1e23, Strtod(vector, 23)); + + CHECK_EQ(1e35, Strtod(vector, 35)); + CHECK_EQ(1e36, Strtod(vector, 36)); + CHECK_EQ(1e37, Strtod(vector, 37)); + CHECK_EQ(1e-1, Strtod(vector, -1)); + CHECK_EQ(1e-2, Strtod(vector, -2)); + CHECK_EQ(1e-5, Strtod(vector, -5)); + CHECK_EQ(1e-20, Strtod(vector, -20)); + CHECK_EQ(1e-22, Strtod(vector, -22)); + CHECK_EQ(1e-23, Strtod(vector, -23)); + CHECK_EQ(1e-25, Strtod(vector, -25)); + CHECK_EQ(1e-39, Strtod(vector, -39)); + + vector = StringToVector("2"); + CHECK_EQ(2.0, Strtod(vector, 0)); + CHECK_EQ(20.0, Strtod(vector, 1)); + CHECK_EQ(200.0, Strtod(vector, 2)); + CHECK_EQ(2e20, Strtod(vector, 20)); + CHECK_EQ(2e22, Strtod(vector, 22)); + CHECK_EQ(2e23, Strtod(vector, 23)); + CHECK_EQ(2e35, Strtod(vector, 35)); + CHECK_EQ(2e36, Strtod(vector, 36)); + CHECK_EQ(2e37, Strtod(vector, 37)); + CHECK_EQ(2e-1, Strtod(vector, -1)); + CHECK_EQ(2e-2, Strtod(vector, -2)); + CHECK_EQ(2e-5, Strtod(vector, -5)); + CHECK_EQ(2e-20, Strtod(vector, -20)); + CHECK_EQ(2e-22, Strtod(vector, -22)); + CHECK_EQ(2e-23, Strtod(vector, -23)); + CHECK_EQ(2e-25, Strtod(vector, -25)); + CHECK_EQ(2e-39, Strtod(vector, -39)); + + vector = StringToVector("9"); + CHECK_EQ(9.0, Strtod(vector, 0)); + CHECK_EQ(90.0, Strtod(vector, 1)); + CHECK_EQ(900.0, Strtod(vector, 2)); + CHECK_EQ(9e20, Strtod(vector, 20)); + CHECK_EQ(9e22, Strtod(vector, 22)); + CHECK_EQ(9e23, Strtod(vector, 23)); + CHECK_EQ(9e35, Strtod(vector, 35)); + CHECK_EQ(9e36, Strtod(vector, 36)); + CHECK_EQ(9e37, Strtod(vector, 37)); + CHECK_EQ(9e-1, Strtod(vector, -1)); + CHECK_EQ(9e-2, Strtod(vector, -2)); + CHECK_EQ(9e-5, Strtod(vector, -5)); + CHECK_EQ(9e-20, Strtod(vector, -20)); + CHECK_EQ(9e-22, Strtod(vector, -22)); + CHECK_EQ(9e-23, Strtod(vector, -23)); + CHECK_EQ(9e-25, Strtod(vector, -25)); + CHECK_EQ(9e-39, Strtod(vector, -39)); + + vector = StringToVector("12345"); + CHECK_EQ(12345.0, Strtod(vector, 0)); + CHECK_EQ(123450.0, Strtod(vector, 1)); + CHECK_EQ(1234500.0, Strtod(vector, 2)); + CHECK_EQ(12345e20, Strtod(vector, 20)); + CHECK_EQ(12345e22, Strtod(vector, 22)); + CHECK_EQ(12345e23, Strtod(vector, 23)); + CHECK_EQ(12345e30, Strtod(vector, 30)); + CHECK_EQ(12345e31, Strtod(vector, 31)); + CHECK_EQ(12345e32, Strtod(vector, 32)); + CHECK_EQ(12345e35, Strtod(vector, 35)); + CHECK_EQ(12345e36, Strtod(vector, 36)); + CHECK_EQ(12345e37, Strtod(vector, 37)); + CHECK_EQ(12345e-1, Strtod(vector, -1)); + CHECK_EQ(12345e-2, Strtod(vector, -2)); + CHECK_EQ(12345e-5, Strtod(vector, -5)); + CHECK_EQ(12345e-20, Strtod(vector, -20)); + CHECK_EQ(12345e-22, Strtod(vector, -22)); + CHECK_EQ(12345e-23, Strtod(vector, -23)); + CHECK_EQ(12345e-25, Strtod(vector, -25)); + CHECK_EQ(12345e-39, Strtod(vector, -39)); + + vector = StringToVector("12345678901234"); + CHECK_EQ(12345678901234.0, Strtod(vector, 0)); + CHECK_EQ(123456789012340.0, Strtod(vector, 1)); + CHECK_EQ(1234567890123400.0, Strtod(vector, 2)); + CHECK_EQ(12345678901234e20, Strtod(vector, 20)); + CHECK_EQ(12345678901234e22, Strtod(vector, 22)); + CHECK_EQ(12345678901234e23, Strtod(vector, 23)); + CHECK_EQ(12345678901234e30, Strtod(vector, 30)); + CHECK_EQ(12345678901234e31, Strtod(vector, 31)); + CHECK_EQ(12345678901234e32, Strtod(vector, 32)); + CHECK_EQ(12345678901234e35, Strtod(vector, 35)); + CHECK_EQ(12345678901234e36, Strtod(vector, 36)); + CHECK_EQ(12345678901234e37, Strtod(vector, 37)); + CHECK_EQ(12345678901234e-1, Strtod(vector, -1)); + CHECK_EQ(12345678901234e-2, Strtod(vector, -2)); + CHECK_EQ(12345678901234e-5, Strtod(vector, -5)); + CHECK_EQ(12345678901234e-20, Strtod(vector, -20)); + CHECK_EQ(12345678901234e-22, Strtod(vector, -22)); + CHECK_EQ(12345678901234e-23, Strtod(vector, -23)); + CHECK_EQ(12345678901234e-25, Strtod(vector, -25)); + CHECK_EQ(12345678901234e-39, Strtod(vector, -39)); + + vector = StringToVector("123456789012345"); + CHECK_EQ(123456789012345.0, Strtod(vector, 0)); + CHECK_EQ(1234567890123450.0, Strtod(vector, 1)); + CHECK_EQ(12345678901234500.0, Strtod(vector, 2)); + CHECK_EQ(123456789012345e20, Strtod(vector, 20)); + CHECK_EQ(123456789012345e22, Strtod(vector, 22)); + CHECK_EQ(123456789012345e23, Strtod(vector, 23)); + CHECK_EQ(123456789012345e35, Strtod(vector, 35)); + CHECK_EQ(123456789012345e36, Strtod(vector, 36)); + CHECK_EQ(123456789012345e37, Strtod(vector, 37)); + CHECK_EQ(123456789012345e39, Strtod(vector, 39)); + CHECK_EQ(123456789012345e-1, Strtod(vector, -1)); + CHECK_EQ(123456789012345e-2, Strtod(vector, -2)); + CHECK_EQ(123456789012345e-5, Strtod(vector, -5)); + CHECK_EQ(123456789012345e-20, Strtod(vector, -20)); + CHECK_EQ(123456789012345e-22, Strtod(vector, -22)); + CHECK_EQ(123456789012345e-23, Strtod(vector, -23)); + CHECK_EQ(123456789012345e-25, Strtod(vector, -25)); + CHECK_EQ(123456789012345e-39, Strtod(vector, -39)); + CHECK_EQ(0.0, StrtodChar("0", 12345)); + + CHECK_EQ(0.0, StrtodChar("", 1324)); + CHECK_EQ(0.0, StrtodChar("000000000", 123)); + CHECK_EQ(0.0, StrtodChar("2", -324)); + CHECK_EQ(4e-324, StrtodChar("3", -324)); + + // It would be more readable to put non-zero literals on the left side (i.e. + // CHECK_EQ(1e-325, StrtodChar("1", -325))), but then Gcc complains that + // they are truncated to zero. + CHECK_EQ(0.0, StrtodChar("1", -325)); + CHECK_EQ(0.0, StrtodChar("1", -325)); + CHECK_EQ(0.0, StrtodChar("20000", -328)); + CHECK_EQ(40000e-328, StrtodChar("30000", -328)); + CHECK_EQ(0.0, StrtodChar("10000", -329)); + CHECK_EQ(0.0, StrtodChar("90000", -329)); + CHECK_EQ(0.0, StrtodChar("000000001", -325)); + CHECK_EQ(0.0, StrtodChar("000000001", -325)); + CHECK_EQ(0.0, StrtodChar("0000000020000", -328)); + CHECK_EQ(40000e-328, StrtodChar("00000030000", -328)); + CHECK_EQ(0.0, StrtodChar("0000000010000", -329)); + CHECK_EQ(0.0, StrtodChar("0000000090000", -329)); + + + // It would be more readable to put the literals (and not double_infinity) + // on the left side (i.e. CHECK_EQ(1e309, StrtodChar("1", 309))), but then Gcc + // complains that the floating constant exceeds range of 'double'. + + CHECK_EQ(double_infinity, StrtodChar("1", 309)); + + CHECK_EQ(1e308, StrtodChar("1", 308)); + CHECK_EQ(1234e305, StrtodChar("1234", 305)); + CHECK_EQ(1234e304, StrtodChar("1234", 304)); + + CHECK_EQ(double_infinity, StrtodChar("18", 307)); + CHECK_EQ(17e307, StrtodChar("17", 307)); + + CHECK_EQ(double_infinity, StrtodChar("0000001", 309)); + + CHECK_EQ(1e308, StrtodChar("00000001", 308)); + + CHECK_EQ(1234e305, StrtodChar("00000001234", 305)); + CHECK_EQ(1234e304, StrtodChar("000000001234", 304)); + CHECK_EQ(double_infinity, StrtodChar("0000000018", 307)); + CHECK_EQ(17e307, StrtodChar("0000000017", 307)); + CHECK_EQ(double_infinity, StrtodChar("1000000", 303)); + CHECK_EQ(1e308, StrtodChar("100000", 303)); + CHECK_EQ(1234e305, StrtodChar("123400000", 300)); + CHECK_EQ(1234e304, StrtodChar("123400000", 299)); + CHECK_EQ(double_infinity, StrtodChar("180000000", 300)); + CHECK_EQ(17e307, StrtodChar("170000000", 300)); + CHECK_EQ(double_infinity, StrtodChar("00000001000000", 303)); + CHECK_EQ(1e308, StrtodChar("000000000000100000", 303)); + CHECK_EQ(1234e305, StrtodChar("00000000123400000", 300)); + CHECK_EQ(1234e304, StrtodChar("0000000123400000", 299)); + CHECK_EQ(double_infinity, StrtodChar("00000000180000000", 300)); + CHECK_EQ(17e307, StrtodChar("00000000170000000", 300)); + CHECK_EQ(1.7976931348623157E+308, StrtodChar("17976931348623157", 292)); + CHECK_EQ(1.7976931348623158E+308, StrtodChar("17976931348623158", 292)); + CHECK_EQ(double_infinity, StrtodChar("17976931348623159", 292)); + + // The following number is the result of 89255.0/1e-22. Both floating-point + // numbers can be accurately represented with doubles. However on Linux,x86 + // the floating-point stack is set to 80bits and the double-rounding + // introduces an error. + CHECK_EQ(89255e-22, StrtodChar("89255", -22)); + + // Some random values. + CHECK_EQ(358416272e-33, StrtodChar("358416272", -33)); + CHECK_EQ(104110013277974872254e-225, + StrtodChar("104110013277974872254", -225)); + + CHECK_EQ(123456789e108, StrtodChar("123456789", 108)); + CHECK_EQ(123456789e109, StrtodChar("123456789", 109)); + CHECK_EQ(123456789e110, StrtodChar("123456789", 110)); + CHECK_EQ(123456789e111, StrtodChar("123456789", 111)); + CHECK_EQ(123456789e112, StrtodChar("123456789", 112)); + CHECK_EQ(123456789e113, StrtodChar("123456789", 113)); + CHECK_EQ(123456789e114, StrtodChar("123456789", 114)); + CHECK_EQ(123456789e115, StrtodChar("123456789", 115)); + + CHECK_EQ(1234567890123456789012345e108, + StrtodChar("1234567890123456789012345", 108)); + CHECK_EQ(1234567890123456789012345e109, + StrtodChar("1234567890123456789012345", 109)); + CHECK_EQ(1234567890123456789012345e110, + StrtodChar("1234567890123456789012345", 110)); + CHECK_EQ(1234567890123456789012345e111, + StrtodChar("1234567890123456789012345", 111)); + CHECK_EQ(1234567890123456789012345e112, + StrtodChar("1234567890123456789012345", 112)); + CHECK_EQ(1234567890123456789012345e113, + StrtodChar("1234567890123456789012345", 113)); + CHECK_EQ(1234567890123456789012345e114, + StrtodChar("1234567890123456789012345", 114)); + CHECK_EQ(1234567890123456789012345e115, + StrtodChar("1234567890123456789012345", 115)); + CHECK_EQ(1234567890123456789052345e108, + StrtodChar("1234567890123456789052345", 108)); + CHECK_EQ(1234567890123456789052345e109, + StrtodChar("1234567890123456789052345", 109)); + CHECK_EQ(1234567890123456789052345e110, + StrtodChar("1234567890123456789052345", 110)); + CHECK_EQ(1234567890123456789052345e111, + StrtodChar("1234567890123456789052345", 111)); + CHECK_EQ(1234567890123456789052345e112, + StrtodChar("1234567890123456789052345", 112)); + CHECK_EQ(1234567890123456789052345e113, + StrtodChar("1234567890123456789052345", 113)); + CHECK_EQ(1234567890123456789052345e114, + StrtodChar("1234567890123456789052345", 114)); + CHECK_EQ(1234567890123456789052345e115, + StrtodChar("1234567890123456789052345", 115)); + CHECK_EQ(5.445618932859895e-255, + StrtodChar("5445618932859895362967233318697132813618813095743952975" + "4392982234069699615600475529427176366709107287468930197" + "8628345413991790019316974825934906752493984055268219809" + "5012176093045431437495773903922425632551857520884625114" + "6241265881735209066709685420744388526014389929047617597" + "0302268848374508109029268898695825171158085457567481507" + "4162979705098246243690189880319928315307816832576838178" + "2563074014542859888710209237525873301724479666744537857" + "9026553346649664045621387124193095870305991178772256504" + "4368663670643970181259143319016472430928902201239474588" + "1392338901353291306607057623202353588698746085415097902" + "6640064319118728664842287477491068264828851624402189317" + "2769161449825765517353755844373640588822904791244190695" + "2998382932630754670573838138825217065450843010498555058" + "88186560731", -1035)); + + // Boundary cases. Boundaries themselves should round to even. + // + // 0x1FFFFFFFFFFFF * 2^3 = 72057594037927928 + // next: 72057594037927936 + // boundary: 72057594037927932 should round up. + CHECK_EQ(72057594037927928.0, StrtodChar("72057594037927928", 0)); + CHECK_EQ(72057594037927936.0, StrtodChar("72057594037927936", 0)); + CHECK_EQ(72057594037927936.0, StrtodChar("72057594037927932", 0)); + CHECK_EQ(72057594037927928.0, StrtodChar("7205759403792793199999", -5)); + CHECK_EQ(72057594037927936.0, StrtodChar("7205759403792793200001", -5)); + + // 0x1FFFFFFFFFFFF * 2^10 = 9223372036854774784 + // next: 9223372036854775808 + // boundary: 9223372036854775296 should round up. + CHECK_EQ(9223372036854774784.0, StrtodChar("9223372036854774784", 0)); + CHECK_EQ(9223372036854775808.0, StrtodChar("9223372036854775808", 0)); + CHECK_EQ(9223372036854775808.0, StrtodChar("9223372036854775296", 0)); + + CHECK_EQ(9223372036854774784.0, StrtodChar("922337203685477529599999", -5)); + CHECK_EQ(9223372036854775808.0, StrtodChar("922337203685477529600001", -5)); + + // 0x1FFFFFFFFFFFF * 2^50 = 10141204801825834086073718800384 + // next: 10141204801825835211973625643008 + // boundary: 10141204801825834649023672221696 should round up. + // + CHECK_EQ(10141204801825834086073718800384.0, + StrtodChar("10141204801825834086073718800384", 0)); + CHECK_EQ(10141204801825835211973625643008.0, + StrtodChar("10141204801825835211973625643008", 0)); + CHECK_EQ(10141204801825835211973625643008.0, + StrtodChar("10141204801825834649023672221696", 0)); + CHECK_EQ(10141204801825834086073718800384.0, + StrtodChar("1014120480182583464902367222169599999", -5)); + CHECK_EQ(10141204801825835211973625643008.0, + StrtodChar("1014120480182583464902367222169600001", -5)); + // 0x1FFFFFFFFFFFF * 2^99 = 5708990770823838890407843763683279797179383808 + // next: 5708990770823839524233143877797980545530986496 + // boundary: 5708990770823839207320493820740630171355185152 + // The boundary should round up. + CHECK_EQ(5708990770823838890407843763683279797179383808.0, + StrtodChar("5708990770823838890407843763683279797179383808", 0)); + CHECK_EQ(5708990770823839524233143877797980545530986496.0, + StrtodChar("5708990770823839524233143877797980545530986496", 0)); + CHECK_EQ(5708990770823839524233143877797980545530986496.0, + StrtodChar("5708990770823839207320493820740630171355185152", 0)); + CHECK_EQ(5708990770823838890407843763683279797179383808.0, + StrtodChar("5708990770823839207320493820740630171355185151999", -3)); + CHECK_EQ(5708990770823839524233143877797980545530986496.0, + StrtodChar("5708990770823839207320493820740630171355185152001", -3)); + + // The following test-cases got some public attention in early 2011 when they + // sent Java and PHP into an infinite loop. + CHECK_EQ(2.225073858507201e-308, StrtodChar("22250738585072011", -324)); + CHECK_EQ(2.22507385850720138309e-308, + StrtodChar("22250738585072011360574097967091319759348195463516456480" + "23426109724822222021076945516529523908135087914149158913" + "03962110687008643869459464552765720740782062174337998814" + "10632673292535522868813721490129811224514518898490572223" + "07285255133155755015914397476397983411801999323962548289" + "01710708185069063066665599493827577257201576306269066333" + "26475653000092458883164330377797918696120494973903778297" + "04905051080609940730262937128958950003583799967207254304" + "36028407889577179615094551674824347103070260914462157228" + "98802581825451803257070188608721131280795122334262883686" + "22321503775666622503982534335974568884423900265498198385" + "48794829220689472168983109969836584681402285424333066033" + "98508864458040010349339704275671864433837704860378616227" + "71738545623065874679014086723327636718751", -1076)); +END_TEST() + + +/* Non-google test */ +BEGIN_TEST(grisu3_print_double) + vector = "13"; + CHECK_EQ(13e-2, Strtod(vector, -2)); + CHECK_EQ(13e-3, Strtod(vector, -3)); + + vector = "-13"; + CHECK_EQ(-13e-2, Strtod(vector, -2)); + CHECK_EQ(-13e-3, Strtod(vector, -3)); + vector = "-1"; + CHECK_EQ(-1e-2, Strtod(vector, -2)); + CHECK_EQ(-1e-3, Strtod(vector, -3)); + + CHECK_EQ(-1e1, StrtodChar("-1", 1)); + CHECK_EQ(-1e+1, StrtodChar("-1", 1)); + CHECK_EQ(-1e-0, StrtodChar("-1", -0)); + CHECK_EQ(-1e-1, StrtodChar("-1", -1)); + CHECK_EQ(-1e-2, StrtodChar("-1", -2)); + CHECK_EQ(-1e-3, StrtodChar("-1", -3)); + CHECK_EQ(-1e-4, StrtodChar("-1", -4)); + + CHECK_EQ(-12e1, StrtodChar("-12", 1)); + CHECK_EQ(-12e+1, StrtodChar("-12", 1)); + CHECK_EQ(-12e-0, StrtodChar("-12", -0)); + CHECK_EQ(-12e-1, StrtodChar("-12", -1)); + CHECK_EQ(-12e-2, StrtodChar("-12", -2)); + CHECK_EQ(-12e-3, StrtodChar("-12", -3)); + CHECK_EQ(-12e-4, StrtodChar("-12", -4)); + + CHECK_EQ(-123e1, StrtodChar("-123", 1)); + CHECK_EQ(-123e+1, StrtodChar("-123", 1)); + CHECK_EQ(-123e-0, StrtodChar("-123", -0)); + CHECK_EQ(-123e-1, StrtodChar("-123", -1)); + CHECK_EQ(-123e-2, StrtodChar("-123", -2)); + CHECK_EQ(-123e-3, StrtodChar("-123", -3)); + CHECK_EQ(-123e-4, StrtodChar("-123", -4)); + + CHECK_EQ(-1234e1, StrtodChar("-1234", 1)); + CHECK_EQ(-1234e+1, StrtodChar("-1234", 1)); + CHECK_EQ(-1234e-0, StrtodChar("-1234", -0)); + CHECK_EQ(-1234e-1, StrtodChar("-1234", -1)); + CHECK_EQ(-1234e-2, StrtodChar("-1234", -2)); + CHECK_EQ(-1234e-3, StrtodChar("-1234", -3)); + CHECK_EQ(-1234e-4, StrtodChar("-1234", -4)); + + CHECK_EQ(-12345e1, StrtodChar("-12345", 1)); + CHECK_EQ(-12345e+1, StrtodChar("-12345", 1)); + CHECK_EQ(-12345e-0, StrtodChar("-12345", -0)); + CHECK_EQ(-12345e-1, StrtodChar("-12345", -1)); + CHECK_EQ(-12345e-2, StrtodChar("-12345", -2)); + CHECK_EQ(-12345e-3, StrtodChar("-12345", -3)); + CHECK_EQ(-12345e-4, StrtodChar("-12345", -4)); + + CHECK_EQ(-12345e-5, StrtodChar("-12345", -5)); + CHECK_EQ(-12345e-6, StrtodChar("-12345", -6)); + CHECK_EQ(-12345e-7, StrtodChar("-12345", -7)); + CHECK_EQ(-12345e-8, StrtodChar("-12345", -8)); + CHECK_EQ(-12345e-9, StrtodChar("-12345", -9)); + CHECK_EQ(-12345e-10, StrtodChar("-12345", -10)); +END_TEST() + +int main() +{ + int fail = 0; + + fail += test_Strtod(); + fail += test_grisu3_print_double(); + + if (fail) { + fprintf(stderr, "FAILURE\n"); + return -1; + } + fprintf(stderr, "SUCCESS\n"); + return 0; +} diff --git a/flatcc/external/grisu3/test.sh b/flatcc/external/grisu3/test.sh new file mode 100755 index 0000000..1794fbb --- /dev/null +++ b/flatcc/external/grisu3/test.sh @@ -0,0 +1,18 @@ +#!/bin/sh + +set -e + +cd $(dirname $0) +mkdir -p build + +CC=cc + +$CC -g -Wall -Wextra $INCLUDE -I.. grisu3_test.c -lm -o build/grisu3_test_d +$CC -DNDEBUG -Wall -Wextra -O2 $INCLUDE -I.. grisu3_test.c -lm -o build/grisu3_test +echo "DEBUG:" +build/grisu3_test_d +echo "OPTIMIZED:" +build/grisu3_test + +echo "running double conversion tests" +./test_dblcnv.sh diff --git a/flatcc/external/grisu3/test_dblcnv.sh b/flatcc/external/grisu3/test_dblcnv.sh new file mode 100755 index 0000000..89f58f4 --- /dev/null +++ b/flatcc/external/grisu3/test_dblcnv.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +set -e + +cd $(dirname $0) +mkdir -p build + +CC=cc + +$CC -g -Wall -Wextra $INCLUDE -I.. grisu3_test_dblcnv.c -o build/grisu3_test_dblcnv_d +$CC -DNDEBUG -Wall -Wextra -O2 $INCLUDE -I.. grisu3_test_dblcnv.c -o build/grisu3_test_dblcnv +echo "DEBUG:" +build/grisu3_test_dblcnv_d +echo "OPTIMIZED:" +build/grisu3_test_dblcnv |