| /* |
| * An internal strtod() implementation based upon V8 src/strtod.cc |
| * without bignum support. |
| * |
| * Copyright 2012 the V8 project authors. All rights reserved. |
| * Use of this source code is governed by a BSD-style license |
| * that can be found in the LICENSE file. |
| */ |
| |
| #include <njs_main.h> |
| #include <njs_diyfp.h> |
| |
| /* |
| * Max double: 1.7976931348623157 x 10^308 |
| * Min non-zero double: 4.9406564584124654 x 10^-324 |
| * Any x >= 10^309 is interpreted as +infinity. |
| * Any x <= 10^-324 is interpreted as 0. |
| * Note that 2.5e-324 (despite being smaller than the min double) |
| * will be read as non-zero (equal to the min non-zero double). |
| */ |
| |
| #define NJS_DECIMAL_POWER_MAX 309 |
| #define NJS_DECIMAL_POWER_MIN (-324) |
| |
| #define NJS_UINT64_MAX njs_uint64(0xFFFFFFFF, 0xFFFFFFFF) |
| #define NJS_UINT64_DECIMAL_DIGITS_MAX 19 |
| |
| |
| /* |
| * Reads digits from the buffer and converts them to a uint64. |
| * Reads in as many digits as fit into a uint64. |
| * When the string starts with "1844674407370955161" no further digit is read. |
| * Since 2^64 = 18446744073709551616 it would still be possible read another |
| * digit if it was less or equal than 6, but this would complicate the code. |
| */ |
| njs_inline uint64_t |
| njs_read_uint64(const u_char *start, size_t length, size_t *ndigits) |
| { |
| u_char d; |
| uint64_t value; |
| const u_char *p, *e; |
| |
| value = 0; |
| |
| p = start; |
| e = p + length; |
| |
| while (p < e && value <= (NJS_UINT64_MAX / 10 - 1)) { |
| d = *p++ - '0'; |
| value = 10 * value + d; |
| } |
| |
| *ndigits = p - start; |
| |
| return value; |
| } |
| |
| |
| /* |
| * Reads a njs_diyfp_t from the buffer. |
| * The returned njs_diyfp_t is not necessarily normalized. |
| * If remaining is zero then the returned njs_diyfp_t is accurate. |
| * Otherwise it has been rounded and has error of at most 1/2 ulp. |
| */ |
| static njs_diyfp_t |
| njs_diyfp_read(const u_char *start, size_t length, int *remaining) |
| { |
| size_t read; |
| uint64_t significand; |
| |
| significand = njs_read_uint64(start, length, &read); |
| |
| /* Round the significand. */ |
| |
| if (length != read) { |
| if (start[read] >= '5') { |
| significand++; |
| } |
| } |
| |
| *remaining = length - read; |
| |
| return njs_diyfp(significand, 0); |
| } |
| |
| |
| /* |
| * Returns 10^exp as an exact njs_diyfp_t. |
| * The given exp must be in the range [1; NJS_DECIMAL_EXPONENT_DIST[. |
| */ |
| njs_inline njs_diyfp_t |
| njs_adjust_pow10(int exp) |
| { |
| switch (exp) { |
| case 1: |
| return njs_diyfp(njs_uint64(0xa0000000, 00000000), -60); |
| case 2: |
| return njs_diyfp(njs_uint64(0xc8000000, 00000000), -57); |
| case 3: |
| return njs_diyfp(njs_uint64(0xfa000000, 00000000), -54); |
| case 4: |
| return njs_diyfp(njs_uint64(0x9c400000, 00000000), -50); |
| case 5: |
| return njs_diyfp(njs_uint64(0xc3500000, 00000000), -47); |
| case 6: |
| return njs_diyfp(njs_uint64(0xf4240000, 00000000), -44); |
| case 7: |
| return njs_diyfp(njs_uint64(0x98968000, 00000000), -40); |
| default: |
| njs_unreachable(); |
| return njs_diyfp(0, 0); |
| } |
| } |
| |
| |
| /* |
| * Returns the significand size for a given order of magnitude. |
| * If v = f*2^e with 2^p-1 <= f <= 2^p then p+e is v's order of magnitude. |
| * This function returns the number of significant binary digits v will have |
| * once its encoded into a double. In almost all cases this is equal to |
| * NJS_SIGNIFICAND_SIZE. The only exception are denormals. They start with |
| * leading zeroes and their effective significand-size is hence smaller. |
| */ |
| njs_inline int |
| njs_diyfp_sgnd_size(int order) |
| { |
| if (order >= (NJS_DBL_EXPONENT_DENORMAL + NJS_SIGNIFICAND_SIZE)) { |
| return NJS_SIGNIFICAND_SIZE; |
| } |
| |
| if (order <= NJS_DBL_EXPONENT_DENORMAL) { |
| return 0; |
| } |
| |
| return order - NJS_DBL_EXPONENT_DENORMAL; |
| } |
| |
| |
| #define NJS_DENOM_LOG 3 |
| #define NJS_DENOM (1 << NJS_DENOM_LOG) |
| |
| /* |
| * Returns either the correct double or the double that is just below |
| * the correct double. |
| */ |
| static double |
| njs_diyfp_strtod(const u_char *start, size_t length, int exp) |
| { |
| int magnitude, prec_digits; |
| int remaining, dec_exp, adj_exp, orig_e, shift; |
| int64_t error; |
| uint64_t prec_bits, half_way; |
| njs_diyfp_t value, pow, adj_pow, rounded; |
| |
| value = njs_diyfp_read(start, length, &remaining); |
| |
| exp += remaining; |
| |
| /* |
| * Since some digits may have been dropped the value is not accurate. |
| * If remaining is different than 0 than the error is at most .5 ulp |
| * (unit in the last place). |
| * Using a common denominator to avoid dealing with fractions. |
| */ |
| |
| error = (remaining == 0 ? 0 : NJS_DENOM / 2); |
| |
| orig_e = value.exp; |
| value = njs_diyfp_normalize(value); |
| error <<= orig_e - value.exp; |
| |
| if (exp < NJS_DECIMAL_EXPONENT_MIN) { |
| return 0.0; |
| } |
| |
| pow = njs_cached_power_dec(exp, &dec_exp); |
| |
| if (dec_exp != exp) { |
| |
| adj_exp = exp - dec_exp; |
| adj_pow = njs_adjust_pow10(exp - dec_exp); |
| value = njs_diyfp_mul(value, adj_pow); |
| |
| if (NJS_UINT64_DECIMAL_DIGITS_MAX - (int) length < adj_exp) { |
| /* |
| * The adjustment power is exact. There is hence only |
| * an error of 0.5. |
| */ |
| error += NJS_DENOM / 2; |
| } |
| } |
| |
| value = njs_diyfp_mul(value, pow); |
| |
| /* |
| * 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 'value' and b with 'pow': |
| * error_b = 0.5 (all cached powers have an error of less than 0.5 ulp), |
| * error_ab = 0 or 1 / NJS_DENOM > error_a * error_b / 2^64. |
| */ |
| |
| error += NJS_DENOM + (error != 0 ? 1 : 0); |
| |
| orig_e = value.exp; |
| value = njs_diyfp_normalize(value); |
| error <<= orig_e - value.exp; |
| |
| /* |
| * Check whether the double's significand changes when the error is added |
| * or substracted. |
| */ |
| |
| magnitude = NJS_DIYFP_SIGNIFICAND_SIZE + value.exp; |
| prec_digits = NJS_DIYFP_SIGNIFICAND_SIZE - njs_diyfp_sgnd_size(magnitude); |
| |
| if (prec_digits + NJS_DENOM_LOG >= NJS_DIYFP_SIGNIFICAND_SIZE) { |
| /* |
| * This can only happen for very small denormals. In this case the |
| * half-way multiplied by the denominator exceeds the range of uint64. |
| * Simply shift everything to the right. |
| */ |
| shift = prec_digits + NJS_DENOM_LOG - NJS_DIYFP_SIGNIFICAND_SIZE + 1; |
| |
| value = njs_diyfp_shift_right(value, shift); |
| |
| /* |
| * Add 1 for the lost precision of error, and NJS_DENOM |
| * for the lost precision of value.significand. |
| */ |
| error = (error >> shift) + 1 + NJS_DENOM; |
| prec_digits -= shift; |
| } |
| |
| prec_bits = value.significand & (((uint64_t) 1 << prec_digits) - 1); |
| prec_bits *= NJS_DENOM; |
| |
| half_way = (uint64_t) 1 << (prec_digits - 1); |
| half_way *= NJS_DENOM; |
| |
| rounded = njs_diyfp_shift_right(value, prec_digits); |
| |
| if (prec_bits >= half_way + error) { |
| rounded.significand++; |
| } |
| |
| return njs_diyfp2d(rounded); |
| } |
| |
| |
| static double |
| njs_strtod_internal(const u_char *start, size_t length, int exp) |
| { |
| int shift; |
| size_t left, right; |
| const u_char *p, *e, *b; |
| |
| /* Trim leading zeroes. */ |
| |
| p = start; |
| e = p + length; |
| |
| while (p < e) { |
| if (*p != '0') { |
| start = p; |
| break; |
| } |
| |
| p++; |
| } |
| |
| left = e - p; |
| |
| /* Trim trailing zeroes. */ |
| |
| b = start; |
| p = b + left - 1; |
| |
| while (p > b) { |
| if (*p != '0') { |
| break; |
| } |
| |
| p--; |
| } |
| |
| right = p - b + 1; |
| |
| length = right; |
| |
| if (length == 0) { |
| return 0.0; |
| } |
| |
| shift = (int) (left - right); |
| |
| if (exp >= NJS_DECIMAL_POWER_MAX - shift - (int) length + 1) { |
| return INFINITY; |
| } |
| |
| if (exp <= NJS_DECIMAL_POWER_MIN - shift - (int) length) { |
| return 0.0; |
| } |
| |
| return njs_diyfp_strtod(start, length, exp + shift); |
| } |
| |
| |
| double |
| njs_strtod(const u_char **start, const u_char *end, njs_bool_t literal) |
| { |
| int exponent, exp, insignf; |
| u_char c, *pos; |
| njs_bool_t minus; |
| const u_char *e, *p, *last, *_; |
| u_char data[128]; |
| |
| exponent = 0; |
| insignf = 0; |
| |
| pos = data; |
| last = data + sizeof(data); |
| |
| p = *start; |
| _ = p - 2; |
| |
| for (; p < end; p++) { |
| /* Values less than '0' become >= 208. */ |
| c = *p - '0'; |
| |
| if (njs_slow_path(c > 9)) { |
| if (literal) { |
| if ((p - _) == 1) { |
| goto done; |
| } |
| |
| if (*p == '_') { |
| _ = p; |
| continue; |
| } |
| } |
| |
| break; |
| } |
| |
| if (pos < last) { |
| *pos++ = *p; |
| |
| } else { |
| insignf++; |
| } |
| } |
| |
| /* Do not emit a '.', but adjust the exponent instead. */ |
| if (p < end && *p == '.') { |
| _ = p; |
| |
| for (p++; p < end; p++) { |
| /* Values less than '0' become >= 208. */ |
| c = *p - '0'; |
| |
| if (njs_slow_path(c > 9)) { |
| if (literal && *p == '_' && (p - _) > 1) { |
| _ = p; |
| continue; |
| } |
| |
| break; |
| } |
| |
| if (pos < last) { |
| *pos++ = *p; |
| exponent--; |
| |
| } else { |
| /* Ignore insignificant digits in the fractional part. */ |
| } |
| } |
| } |
| |
| if (pos == data) { |
| return NAN; |
| } |
| |
| e = p + 1; |
| |
| if (e < end && (*p == 'e' || *p == 'E')) { |
| minus = 0; |
| |
| if (e + 1 < end) { |
| if (*e == '-') { |
| e++; |
| minus = 1; |
| |
| } else if (*e == '+') { |
| e++; |
| } |
| } |
| |
| /* Values less than '0' become >= 208. */ |
| c = *e - '0'; |
| |
| if (njs_fast_path(c <= 9)) { |
| exp = c; |
| |
| for (p = e + 1; p < end; p++) { |
| /* Values less than '0' become >= 208. */ |
| c = *p - '0'; |
| |
| if (njs_slow_path(c > 9)) { |
| if (literal && *p == '_' && (p - _) > 1) { |
| _ = p; |
| continue; |
| } |
| |
| break; |
| } |
| |
| if (exp < (INT_MAX - 9) / 10) { |
| exp = exp * 10 + c; |
| } |
| } |
| |
| exponent += minus ? -exp : exp; |
| |
| } else if (literal && *e == '_') { |
| p = e; |
| } |
| } |
| |
| done: |
| |
| *start = p; |
| |
| exponent += insignf; |
| |
| return njs_strtod_internal(data, pos - data, exponent); |
| } |