762 lines
26 KiB
C
762 lines
26 KiB
C
|
/* emyg_atod.c
|
||
|
**
|
||
|
** Copyright (C) 2015 Doug Currie, Londonderry, NH, USA
|
||
|
**
|
||
|
** Permission is hereby granted, free of charge, to any person obtaining a copy
|
||
|
** of this software and associated documentation files (the "Software"), to deal
|
||
|
** in the Software without restriction, including without limitation the rights
|
||
|
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||
|
** copies of the Software, and to permit persons to whom the Software is
|
||
|
** furnished to do so, subject to the following conditions:
|
||
|
**
|
||
|
** The above copyright notice and this permission notice shall be included in
|
||
|
** all copies or substantial portions of the Software.
|
||
|
**
|
||
|
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
|
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
|
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||
|
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||
|
** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||
|
** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||
|
** THE SOFTWARE.
|
||
|
*/
|
||
|
|
||
|
/* This code is an implementation of strtod() to accompany emyg_dtoa.c
|
||
|
** It is based on an algorithm described by Aubrey Jaffer, January 2015,
|
||
|
** "Easy Accurate Reading and Writing of Floating-Point Numbers"
|
||
|
** arXiv:1310.8121v6 -- http://arxiv.org/abs/1310.8121v6
|
||
|
*/
|
||
|
/* This implementation, besides the obvious translation from Java to C, has
|
||
|
** some differences/improvements:
|
||
|
** 1. All memory allocations are on the stack, no heap allocations
|
||
|
** 2. We don't shift numerator right for the case: negative dpoint, positive bex
|
||
|
** This can happen for an input like -139745422447689500.0 that has...
|
||
|
** mant: 1397454224476895000 dp: -1 bex: 5. Shifting the mantissa right would
|
||
|
** lose information; instead we shift the scale factor left.
|
||
|
** 3. We use a roundQuotient function (quornd) optimized for atod; it adjusts
|
||
|
** the result to be 53 bits if it is 54 before rounding by appropriate use
|
||
|
** of the quotient LSB and remainder; this avoids doing the divide twice.
|
||
|
*/
|
||
|
|
||
|
#include <stdlib.h>
|
||
|
#include <limits.h>
|
||
|
#include <stdint.h>
|
||
|
#include <ctype.h> // used by emyg_strtod
|
||
|
#include <math.h> // scalb()
|
||
|
|
||
|
#include "emyg_pow5.h" // in separate file to support alternate implementations, and it's big
|
||
|
|
||
|
#ifdef TESTING_QUOREM
|
||
|
#include <stdio.h>
|
||
|
#include <string.h>
|
||
|
#define QUODBG(x) x
|
||
|
#else
|
||
|
#define QUODBG(x)
|
||
|
#endif
|
||
|
|
||
|
#define OPTIMIZE_FOR_ATOD
|
||
|
|
||
|
#define BIGNUM_JUMBO_SIZE_UINT32 (30) /* 960 bits > (64 + 54 + (log (expt 5 345) 2) = 919) */
|
||
|
#define BIGNUM_NORMAL_SIZE_UINT32 (28) /* when we don't need the extra space for multiply */
|
||
|
#define BIGNUM_QUOTIENT_SIZE_UINT32 (4) /* difference twixt dividend - divisor; see m - n + 1 */
|
||
|
|
||
|
static const int doubleMantissaBits = 53;
|
||
|
|
||
|
static inline int nlz32 (uint32_t x)
|
||
|
{
|
||
|
#if defined(_MSC_VER) && defined(_M_AMD64)
|
||
|
unsigned long bitno;
|
||
|
uint8_t s = _BitScanReverse(&bitno, (unsigned long )x);
|
||
|
return s ? (31 - (int )bitno) : 32;
|
||
|
#elif defined(__GNUC__) && (UINT_MAX == 0xFFFFFFFFUL)
|
||
|
return (x == 0u) ? 32 : __builtin_clz((unsigned int )x);
|
||
|
#elif defined(__GNUC__) && (ULONG_MAX == 0xFFFFFFFFUL)
|
||
|
return (x == 0u) ? 32 : __builtin_clzl((unsigned long )x);
|
||
|
#else
|
||
|
int n;
|
||
|
if (x == 0) return(32);
|
||
|
n = 0;
|
||
|
if (x <= 0x0000FFFF) { n = n +16; x = x <<16; }
|
||
|
if (x <= 0x00FFFFFF) { n = n + 8; x = x << 8; }
|
||
|
if (x <= 0x0FFFFFFF) { n = n + 4; x = x << 4; }
|
||
|
if (x <= 0x3FFFFFFF) { n = n + 2; x = x << 2; }
|
||
|
if (x <= 0x7FFFFFFF) { n = n + 1; }
|
||
|
return n;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
static inline int nlz64 (uint64_t x)
|
||
|
{
|
||
|
#if defined(_MSC_VER) && defined(_M_AMD64)
|
||
|
unsigned long bitno;
|
||
|
uint8_t s = _BitScanReverse64(&bitno, x);
|
||
|
return s ? (63 - (int )bitno) : 64;
|
||
|
#elif defined(__GNUC__) && (ULONG_MAX > 0xFFFFFFFFFFFFFFF5ULL)
|
||
|
return (x == 0u) ? 64 : __builtin_clzl((unsigned long )x);
|
||
|
#elif defined(__GNUC__) && (ULONGLONG_MAX > 0xFFFFFFFFFFFFFFF5ULL)
|
||
|
return (x == 0u) ? 64 : __builtin_clzll((unsigned long long )x);
|
||
|
#else
|
||
|
int s = nlz32(x >> 32);
|
||
|
return (s == 32) ? (32 + nlz32(x & UINT32_MAX)) : s;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
/* Divide code divmnu.c adapted from Hacker's Delight; here are the changes:
|
||
|
** 1. Eliminated signed arithmetic (shift left and conversions) since
|
||
|
** these are undefined behavior for C; adapted similar solution from
|
||
|
** David Ireland's BigDigits library.
|
||
|
** 2. Removed the option to return the remainder, and added the rounding
|
||
|
** feature needed for IEEE conversion.
|
||
|
** 3. Eliminated the alloca() calls; we know that for IEEE doubles none
|
||
|
** of our bignums will be larger than 112 bits.
|
||
|
** 4. Added code specific to emyg_atod() to force result size to 53 bits
|
||
|
** and returning a -1 to indicate the scaling by /2
|
||
|
** Hacker's Delight web site has a permission statement, incuding: you are
|
||
|
** free to use, copy, and distribute any of the code on this web site,
|
||
|
** whether modified by you or not. You need not give attribution.
|
||
|
*/
|
||
|
|
||
|
/* This divides an n-word dividend by an m-word divisor, giving an
|
||
|
** n-m+1-word quotient and m-word remainder. The bignums are in arrays of
|
||
|
** words. Here a "word" is 32 bits. This routine is designed for a 64-bit
|
||
|
** machine which has a 64/64 division instruction. */
|
||
|
|
||
|
/* q[0], u[0], and v[0] contain the LEAST significant words.
|
||
|
** (The sequence is in little-endian order).
|
||
|
**
|
||
|
** This is a fairly precise implementation of Knuth's Algorithm D, for a
|
||
|
** binary computer with base b = 2**32. The caller supplies:
|
||
|
** 1. Space q for the quotient, m - n + 1 words (at least one).
|
||
|
** 2. The dividend u, m words, m >= 1.
|
||
|
** 3. The divisor v, n words, n >= 1.
|
||
|
** The most significant digit of the divisor, v[n-1], must be nonzero. The
|
||
|
** dividend u may have leading zeros; this just makes the algorithm take
|
||
|
** longer and makes the quotient contain more leading zeros.
|
||
|
** The program does not alter the input parameters u and v.
|
||
|
** The quotient returned may have leading zeros. The
|
||
|
** function itself returns a value of 0 for success and 1 for invalid
|
||
|
** parameters (e.g., division by 0).
|
||
|
** For now, we must have m >= n. Knuth's Algorithm D also requires
|
||
|
** that the dividend be at least as long as the divisor. (In his terms,
|
||
|
** m >= 0 (unstated). Therefore m+n >= n.)
|
||
|
*/
|
||
|
|
||
|
int quornd (uint32_t q[], const uint32_t u[], const uint32_t v[], int m, int n)
|
||
|
{
|
||
|
const uint64_t b = 4294967296ULL; // Number base (2**32).
|
||
|
uint32_t un[BIGNUM_JUMBO_SIZE_UINT32]; // Normalized form of u, v.
|
||
|
uint32_t vn[BIGNUM_JUMBO_SIZE_UINT32];
|
||
|
uint64_t qhat; // Estimated quotient digit.
|
||
|
uint64_t rhat; // A remainder.
|
||
|
uint64_t p; // Product of two digits.
|
||
|
uint64_t t, k;
|
||
|
int s, i, j;
|
||
|
int res = 0;
|
||
|
|
||
|
if (m < n || n <= 0 || v[n-1] == 0)
|
||
|
return 1; // Return if invalid param.
|
||
|
|
||
|
if (n == 1)
|
||
|
{
|
||
|
k = 0; // Take care of the case of a
|
||
|
for (j = m - 1; j >= 0; j--) // single-digit divisor here.
|
||
|
{
|
||
|
q[j] = (k*b + u[j]) / v[0];
|
||
|
k = (k*b + u[j]) - (q[j] * v[0]);
|
||
|
}
|
||
|
#ifdef OPTIMIZE_FOR_ATOD
|
||
|
if ((64 - nlz32(q[1])) > doubleMantissaBits)
|
||
|
{
|
||
|
// we need to divide quotient by 2 to make it fit
|
||
|
res = -1; // let caller know we made this adjustment
|
||
|
uint32_t saved_q0 = q[0];
|
||
|
q[0] = (q[0] >> 1) | (q[1] << 31);
|
||
|
q[1] = q[1] >> 1;
|
||
|
|
||
|
if (0 == (saved_q0 & 1))
|
||
|
return res; // no need to round
|
||
|
// now the remainder is >= 0.5
|
||
|
// if un is 0, then the remainder is 0.5
|
||
|
// otherwise it is > 0.5
|
||
|
if ((k & UINT32_MAX) != 0)
|
||
|
goto round_up; // (2 * remainder) > divisor, round
|
||
|
// continue with check for round_even
|
||
|
if ((q[0] & 1) == 1)
|
||
|
goto round_up; // round to even
|
||
|
return res;
|
||
|
}
|
||
|
#endif
|
||
|
// rounding
|
||
|
k = (k & UINT32_MAX) * 2; // k is now 2 * remainder
|
||
|
if ((k > v[0]) || ((k == v[0]) && ((q[0] & 1) == 1)))
|
||
|
{
|
||
|
round_up:
|
||
|
i = 0;
|
||
|
do
|
||
|
{
|
||
|
t = (uint64_t)q[i] + 1;
|
||
|
q[i++] = t;
|
||
|
} while ((t > UINT32_MAX) && (i < (m - n + 1)));
|
||
|
}
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
/* Normalize by shifting v left just enough so that its high-order
|
||
|
bit is on, and shift u left the same amount. We may have to append a
|
||
|
high-order digit on the dividend; we do that unconditionally. */
|
||
|
|
||
|
s = nlz32(v[n-1]); // 0 <= s <= 31.
|
||
|
|
||
|
if (n > BIGNUM_JUMBO_SIZE_UINT32) return 1;
|
||
|
for (i = n - 1; i > 0; i--)
|
||
|
vn[i] = (v[i] << s) | ((uint64_t)v[i-1] >> (32-s));
|
||
|
vn[0] = v[0] << s;
|
||
|
|
||
|
if ((m + 1) > BIGNUM_JUMBO_SIZE_UINT32) return 1;
|
||
|
un[m] = (uint64_t)u[m-1] >> (32-s);
|
||
|
for (i = m - 1; i > 0; i--)
|
||
|
un[i] = (u[i] << s) | ((uint64_t)u[i-1] >> (32-s));
|
||
|
un[0] = u[0] << s;
|
||
|
|
||
|
for (j = m - n; j >= 0; j--) // Main loop.
|
||
|
{
|
||
|
// Compute estimate qhat of q[j].
|
||
|
qhat = (un[j+n]*b + un[j+n-1]) / vn[n-1];
|
||
|
rhat = (un[j+n]*b + un[j+n-1]) - qhat*vn[n-1];
|
||
|
again:
|
||
|
if (qhat >= b || qhat*vn[n-2] > b*rhat + un[j+n-2])
|
||
|
{
|
||
|
qhat = qhat - 1;
|
||
|
rhat = rhat + vn[n-1];
|
||
|
if (rhat < b) goto again;
|
||
|
}
|
||
|
|
||
|
// Multiply and subtract.
|
||
|
k = 0;
|
||
|
for (i = 0; i < n; i++)
|
||
|
{
|
||
|
p = qhat * vn[i];
|
||
|
un[i+j] -= k;
|
||
|
k = (un[i+j] > (UINT32_MAX - k)) ? 1 : 0;
|
||
|
un[i+j] -= (p & UINT32_MAX);
|
||
|
if (un[i+j] > (UINT32_MAX - (p & UINT32_MAX))) k++;
|
||
|
k += (p >> 32);
|
||
|
}
|
||
|
un[j+n] -= k;
|
||
|
|
||
|
q[j] = qhat; // Store quotient digit.
|
||
|
if (un[j+n]) // If we subtracted too
|
||
|
{
|
||
|
q[j] = q[j] - 1; // much, add back.
|
||
|
k = 0;
|
||
|
for (i = 0; i < n; i++)
|
||
|
{
|
||
|
t = (uint64_t)un[i+j] + vn[i] + k;
|
||
|
un[i+j] = t;
|
||
|
k = t >> 32;
|
||
|
}
|
||
|
un[j+n] = un[j+n] + k;
|
||
|
}
|
||
|
} // End j.
|
||
|
|
||
|
#ifdef OPTIMIZE_FOR_ATOD
|
||
|
if ((64 - nlz32(q[1])) > doubleMantissaBits)
|
||
|
{
|
||
|
// we need to divide quotient by 2 to make it fit
|
||
|
res = -1; // let caller know we made this adjustment
|
||
|
uint32_t saved_q0 = q[0];
|
||
|
q[0] = (q[0] >> 1) | (q[1] << 31);
|
||
|
q[1] = q[1] >> 1;
|
||
|
|
||
|
if (0 == (saved_q0 & 1))
|
||
|
return -1; // no need to round
|
||
|
// now the remainder is >= 0.5
|
||
|
// if un is 0, then the remainder is 0.5
|
||
|
// otherwise it is > 0.5
|
||
|
for (i = n-1; i >= 0; i--)
|
||
|
{
|
||
|
if (un[i] != 0)
|
||
|
goto round_up; // (2 * remainder) > divisor, round
|
||
|
}
|
||
|
// continue with check for round_even
|
||
|
}
|
||
|
else
|
||
|
#endif
|
||
|
{
|
||
|
// Rounding: multiply the remainder by 2 and compare with the divisor
|
||
|
//
|
||
|
if (un[n-1] > (UINT32_MAX / 2u))
|
||
|
goto round_up;
|
||
|
|
||
|
for (i = n-1; i > 0; i--)
|
||
|
{
|
||
|
uint32_t ud = (un[i] << 1) | (un[i-1] >> 31);
|
||
|
if (ud > vn[i])
|
||
|
goto round_up; // (2 * remainder) > divisor, round
|
||
|
if (ud < vn[i])
|
||
|
return 0; // (2 * remainder) < divisor, done
|
||
|
}
|
||
|
if ((un[0] << 1) > vn[0])
|
||
|
goto round_up; // (2 * remainder) > divisor, round
|
||
|
if ((un[0] << 1) < vn[0])
|
||
|
return 0; // (2 * remainder) < divisor, done
|
||
|
}
|
||
|
// Check for round to even
|
||
|
// (2 * remainder) == divisor
|
||
|
if ((q[0] & 1) == 1) goto round_up; // round to even
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
#if TESTING_QUOREM
|
||
|
void print_bigint (const char *nm, const uint32_t num[], const int n)
|
||
|
{
|
||
|
int i;
|
||
|
printf("%s: ", nm);
|
||
|
for (i = 0; i < n; i++)
|
||
|
printf("%u ", num[i]);
|
||
|
printf("\n");
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
static inline void one_shiftLeft (uint32_t v[], int s)
|
||
|
{
|
||
|
int x = 0;
|
||
|
while (s >= 32) { v[x++] = 0; s -= 32; }
|
||
|
v[x] = 1u << s;
|
||
|
}
|
||
|
|
||
|
static inline void scl_shift_left_by (uint32_t v[], const uint32_t x[], int sz, int s)
|
||
|
{
|
||
|
// v and x may be identical
|
||
|
int w = s / 32; // words to shift
|
||
|
int b = s % 32; // bits to shift
|
||
|
int i;
|
||
|
|
||
|
QUODBG(printf("scl_shift_left_by sz %d shift %d (%d %d)\n", sz, s, w, b));
|
||
|
|
||
|
// sz is the size of the result, v
|
||
|
// x is guaranteed to be appropriately sized to provide enough data
|
||
|
// this is a dumb api but works for now
|
||
|
|
||
|
// first copy words
|
||
|
v[sz - 1] = 0u;
|
||
|
for (i = sz - 2; i >= 0; i--)
|
||
|
{
|
||
|
v[i] = ((i - w) >= 0) ? x[i - w] : 0u;
|
||
|
}
|
||
|
|
||
|
// then shift bits
|
||
|
for (i = sz - 1; i > 0; i--)
|
||
|
{
|
||
|
v[i] = (v[i] << b) | (v[i - 1] >> (32 - b));
|
||
|
}
|
||
|
v[0] = (v[0] << b);
|
||
|
}
|
||
|
|
||
|
static inline void u64_shiftLeft (uint32_t v[], uint64_t n, int sz, int s)
|
||
|
{
|
||
|
uint64_t ns;
|
||
|
int x = 0;
|
||
|
|
||
|
if (s >= 0)
|
||
|
{
|
||
|
while (s >= 32) { v[x++] = 0; s -= 32; }
|
||
|
ns = n << s;
|
||
|
v[x++] = (ns & UINT32_MAX);
|
||
|
if (x < sz) // -- don't write past end of v[], caller determined size needed
|
||
|
{
|
||
|
v[x++] = (ns >> 32);
|
||
|
if (x < sz && s != 0) // no more data if s == 0
|
||
|
v[x] = n >> (64 - s);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static inline double doubleValue (uint32_t v[])
|
||
|
{
|
||
|
// only the fist 64-bits of v[] are used; caller has determined that's all that's needed
|
||
|
return (double )((uint64_t )v[0] + ((uint64_t )v[1] << 32));
|
||
|
}
|
||
|
|
||
|
static inline int bitLength (const uint32_t v[], int sz_in_32bit_words)
|
||
|
{
|
||
|
int x = sz_in_32bit_words - 1;
|
||
|
|
||
|
while ((v[x] == 0) && (x > 0)) x = x - 1;
|
||
|
return (x * 32) + (32 - nlz32(v[x]));
|
||
|
}
|
||
|
|
||
|
static inline void mulbyu64 (uint32_t p[], const uint64_t u64mant, const uint32_t m[], int z)
|
||
|
{
|
||
|
// p is z+2 32-bit words
|
||
|
// m is z 32-bit words
|
||
|
|
||
|
// TODO: could this be optimized to use 128 bit math on platforms that support it?
|
||
|
|
||
|
uint64_t lo = u64mant & UINT32_MAX;
|
||
|
uint64_t hi = u64mant >> 32;
|
||
|
uint64_t a = 0;
|
||
|
int i;
|
||
|
|
||
|
for (i = 0; i < z; i++)
|
||
|
{
|
||
|
a += lo * m[i];
|
||
|
p[i] = a & UINT32_MAX;
|
||
|
a = a >> 32;
|
||
|
}
|
||
|
p[z] = a;
|
||
|
//p[z+1] = 0;
|
||
|
a = 0;
|
||
|
if (hi > 0) for (i = 0; i < z; i++)
|
||
|
{
|
||
|
a += (hi * m[i]) + p[i+1]; // this always (just) fits in 64 bits
|
||
|
p[i+1] = (a & UINT32_MAX);
|
||
|
a = a >> 32;
|
||
|
}
|
||
|
p[z+1] = a;
|
||
|
}
|
||
|
|
||
|
/* atod_guts
|
||
|
** input: u64mant dpoint
|
||
|
** result: u64mant * 10**dpoint
|
||
|
**
|
||
|
** case dpoint >= 0:
|
||
|
** reframe as: u64mant * 5**dpoint * 2**dpoint
|
||
|
** if (u64mant * 5**dpoint) is less than 53 bits
|
||
|
** then the answer is just scalb(u64mant * 5**dpoint, dpoint)
|
||
|
** else
|
||
|
** calculate bex = the bit length of (u64mant * 5**dpoint)
|
||
|
** divide and round: (u64mant * 5**dpoint) / 2**bex
|
||
|
** the answer is scalb(u64mant * 5**dpoint, bex + dpoint)
|
||
|
** case dpoint < 0
|
||
|
** reframe as: (u64mant / (5**(-dpoint)) / 2**(-dpoint)
|
||
|
*/
|
||
|
double atod_guts (uint64_t u64mant, int dpoint)
|
||
|
{
|
||
|
const uint32_t *pow5p;
|
||
|
uint32_t num[BIGNUM_JUMBO_SIZE_UINT32]; // up to (log (expt 5 325) 2) = 755 bits => ~104 bytes
|
||
|
uint32_t scl[BIGNUM_NORMAL_SIZE_UINT32];
|
||
|
uint32_t quo[BIGNUM_QUOTIENT_SIZE_UINT32];
|
||
|
int n, m; // as per quornd: n is size of divisor, m is size of dividend in 32-bit words
|
||
|
int z; // z is size of pow5p in 32-bit words
|
||
|
int bex; // binary exponentish
|
||
|
|
||
|
QUODBG(fprintf(stderr, "mant: %llu dp: %d\n", u64mant, dpoint));
|
||
|
|
||
|
if (dpoint >= 0)
|
||
|
{
|
||
|
int r = get_pow5(dpoint, &pow5p, &z);
|
||
|
if (r) return 0.0/0.0; // NaN for bad input -- TODO: use inf for excessive +exponents?
|
||
|
m = z + 2; // size is sum of lengths of multiplicands
|
||
|
if (m > BIGNUM_JUMBO_SIZE_UINT32) { n = 0; goto atod_fail; }
|
||
|
mulbyu64(num, u64mant, pow5p, z); // num = pow5 * u64mant
|
||
|
bex = bitLength(num, m) - doubleMantissaBits;
|
||
|
if (bex <= 0) return scalb(doubleValue(num), dpoint);
|
||
|
QUODBG(fprintf(stderr, "+ bex: %d z: %d m: %d\n", bex, z, m));
|
||
|
n = (bex / 32) + 1; // 32-bit words needed to hold 1 << bex
|
||
|
m = (bex + doubleMantissaBits + 31) / 32; // we may be able to shrink by a word
|
||
|
if (n > BIGNUM_NORMAL_SIZE_UINT32) goto atod_fail;
|
||
|
one_shiftLeft(scl, bex);
|
||
|
if ((m - n + 1) > BIGNUM_QUOTIENT_SIZE_UINT32) goto atod_fail;
|
||
|
QUODBG(fprintf(stderr, "+ n: %d m: %d q: %d\n", n, m, m - n + 1));
|
||
|
if (quornd(quo, num, scl, m, n))
|
||
|
{
|
||
|
QUODBG(fprintf(stderr, "+ quornd returned error\n"));
|
||
|
return 0.0/0.0; // NaN for bad input or software error
|
||
|
}
|
||
|
QUODBG(print_bigint("num", num, m));
|
||
|
QUODBG(print_bigint("scl", scl, n));
|
||
|
QUODBG(print_bigint("quo", quo, m - n + 1));
|
||
|
return scalb(doubleValue(quo), bex + dpoint);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
int bma = 64 - nlz64(u64mant); // bits in mantissa
|
||
|
int r = get_pow5(-dpoint, &pow5p, &z);
|
||
|
if (r) return 0.0/0.0; // NaN for bad input -- TODO: use 0.0 for excessive -exponents?
|
||
|
bex = bma - bitLength(pow5p, z) - doubleMantissaBits;
|
||
|
if (bex > 0)
|
||
|
{
|
||
|
// to avoid losing significant bits, which could occur in the u64_shiftLeft below
|
||
|
// instead of shifting num right, let's shift pow5 left
|
||
|
// DANGER Will Robinson! We are aliasing pow5p and scl -- this is OK since
|
||
|
// the only place below they are both used in the same statement or call is
|
||
|
// scl_shift_left_by(), which is designed to handle aliased pointers
|
||
|
// and which doesn't happen at all if OPTIMIZE_FOR_ATOD is defined
|
||
|
//
|
||
|
m = 2;
|
||
|
if (m > BIGNUM_JUMBO_SIZE_UINT32) { n = 0; goto atod_fail; }
|
||
|
num[0] = u64mant & UINT32_MAX;
|
||
|
num[1] = u64mant >> 32;
|
||
|
// use z+1 because scl_shift_left_by my shift by as many as 10 bits (64 - 1 - 53)
|
||
|
if ((z + 1) > BIGNUM_NORMAL_SIZE_UINT32) { n = 0; goto atod_fail; }
|
||
|
scl_shift_left_by(scl, pow5p, z + 1, bex);
|
||
|
QUODBG(print_bigint("scl", scl, z + 1));
|
||
|
n = (bitLength(scl, z + 1) + 31) / 32;
|
||
|
pow5p = scl; // DANGER Will Robinson! -- see above
|
||
|
QUODBG(fprintf(stderr, "- bma %d bex: %d z: %d m: %d n: %d\n", bma, bex, z, m, n));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
m = (((bma - bex) + 31) / 32); // u64mant << -bex
|
||
|
if (m > BIGNUM_JUMBO_SIZE_UINT32) { n = 0; goto atod_fail; }
|
||
|
QUODBG(fprintf(stderr, "- bma %d bex: %d z: %d m: %d\n", bma, bex, z, m));
|
||
|
u64_shiftLeft(num, u64mant, m, -bex);
|
||
|
n = z;
|
||
|
}
|
||
|
if ((m - n + 1) > BIGNUM_QUOTIENT_SIZE_UINT32) goto atod_fail;
|
||
|
QUODBG(fprintf(stderr, "- n: %d m: %d q: %d\n", n, m, m - n + 1));
|
||
|
r = quornd(quo, num, pow5p, m, n);
|
||
|
if (r > 0)
|
||
|
{
|
||
|
QUODBG(fprintf(stderr, "- quornd returned error\n"));
|
||
|
return 0.0/0.0; // NaN for bad input
|
||
|
}
|
||
|
QUODBG(print_bigint("num", num, m));
|
||
|
QUODBG(print_bigint("pow5p", pow5p, n));
|
||
|
QUODBG(print_bigint("quo", quo, m - n + 1));
|
||
|
#ifdef OPTIMIZE_FOR_ATOD
|
||
|
QUODBG(fprintf(stderr, "- r: %d\n", r));
|
||
|
if (r < 0)
|
||
|
{
|
||
|
bex++;
|
||
|
}
|
||
|
#else
|
||
|
bma = 64 - nlz64(doubleValue(quo));
|
||
|
if (bma > doubleMantissaBits)
|
||
|
{
|
||
|
bex++;
|
||
|
n = (pow5p[z-1] > (UINT32_MAX / 2u)) ? z + 1 : z;
|
||
|
// use z+2 because mulbyu64 will set those words even though they may be zero
|
||
|
if ((z + 2) > BIGNUM_NORMAL_SIZE_UINT32) goto atod_fail;
|
||
|
QUODBG(fprintf(stderr, "- n: %d m: %d q: %d bma: %d\n", n, m, m - n + 1, bma));
|
||
|
scl_shift_left_by(scl, pow5p, z + 1, 1);
|
||
|
if (quornd(quo, num, scl, m, n))
|
||
|
{
|
||
|
QUODBG(fprintf(stderr, "- quornd returned error; v[n-1]: %u\n", scl[n-1]));
|
||
|
return 0.0/0.0; // NaN for bad input
|
||
|
}
|
||
|
QUODBG(print_bigint("num", num, m));
|
||
|
QUODBG(print_bigint("scl", scl, n));
|
||
|
QUODBG(print_bigint("quo", quo, m - n + 1));
|
||
|
}
|
||
|
#endif
|
||
|
return scalb(doubleValue(quo), bex + dpoint);
|
||
|
}
|
||
|
atod_fail:
|
||
|
QUODBG(fprintf(stderr, "atod_guts undersized bignum n: %d m: %d\n", n, m));
|
||
|
return 0.0/0.0; // NaN for bad input
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
double strtod(const char *nptr, char **endptr);
|
||
|
The expected form of the (initial portion of the) string is optional
|
||
|
leading white space as recognized by isspace(3), an optional plus
|
||
|
('+') or minus sign ('-') and then either (i) a decimal number, or
|
||
|
(ii) a hexadecimal number, or (iii) an infinity, or (iv) a NAN (not-
|
||
|
a-number).
|
||
|
|
||
|
A decimal number consists of a nonempty sequence of decimal digits
|
||
|
possibly containing a radix character (decimal point, locale-
|
||
|
dependent, usually '.'), optionally followed by a decimal exponent.
|
||
|
A decimal exponent consists of an 'E' or 'e', followed by an optional
|
||
|
plus or minus sign, followed by a nonempty sequence of decimal
|
||
|
digits, and indicates multiplication by a power of 10.
|
||
|
|
||
|
A hexadecimal number consists of a "0x" or "0X" followed by a
|
||
|
nonempty sequence of hexadecimal digits possibly containing a radix
|
||
|
character, optionally followed by a binary exponent. A binary
|
||
|
exponent consists of a 'P' or 'p', followed by an optional plus or
|
||
|
minus sign, followed by a nonempty sequence of decimal digits, and
|
||
|
indicates multiplication by a power of 2. At least one of radix
|
||
|
character and binary exponent must be present.
|
||
|
|
||
|
An infinity is either "INF" or "INFINITY", disregarding case.
|
||
|
|
||
|
A NAN is "NAN" (disregarding case) optionally followed by a string,
|
||
|
(n-char-sequence), where n-char-sequence specifies in an
|
||
|
implementation-dependent way the type of NAN (see NOTES).
|
||
|
|
||
|
These functions return the converted value, if any.
|
||
|
|
||
|
If endptr is not NULL, a pointer to the character after the last
|
||
|
character used in the conversion is stored in the location referenced
|
||
|
by endptr.
|
||
|
|
||
|
If no conversion is performed, zero is returned and the value of nptr
|
||
|
is stored in the location referenced by endptr.
|
||
|
|
||
|
If the correct value would cause overflow, plus or minus HUGE_VAL
|
||
|
(HUGE_VALF, HUGE_VALL) is returned (according to the sign of the
|
||
|
value), and ERANGE is stored in errno. If the correct value would
|
||
|
cause underflow, zero is returned and ERANGE is stored in errno.
|
||
|
*/
|
||
|
|
||
|
double emyg_strtod(const char *nptr, char **endptr)
|
||
|
{
|
||
|
const char *cp = nptr;
|
||
|
double res;
|
||
|
uint64_t mant; // mantissa
|
||
|
int minus = 0; // mantissa minus
|
||
|
int dadp = 0; // digits after decimal point
|
||
|
int expt = 0; // explicit exponent
|
||
|
int expm = 0; // exponent minus
|
||
|
char c;
|
||
|
|
||
|
while (isspace(c = *cp++)) /*skip*/;
|
||
|
|
||
|
if ('-' == c) { minus = 1; c = *cp++; }
|
||
|
else if ('+' == c) { c = *cp++; }
|
||
|
else {}
|
||
|
|
||
|
if (('n' == c || 'N' == c)
|
||
|
&& ('a' == cp[0] || 'A' == cp[0])
|
||
|
&& ('n' == cp[1] || 'N' == cp[1]))
|
||
|
{
|
||
|
// for Scheme allow (require?) ".0"
|
||
|
if ('.' == cp[2] && '0' == cp[3]) cp = &cp[4];
|
||
|
else cp = &cp[2];
|
||
|
res = 0.0/0.0;
|
||
|
}
|
||
|
else
|
||
|
if (('i' == c || 'I' == c)
|
||
|
&& ('n' == cp[0] || 'N' == cp[0])
|
||
|
&& ('f' == cp[1] || 'F' == cp[1]))
|
||
|
{
|
||
|
// for Scheme allow (require?) ".0"
|
||
|
if ('.' == cp[2] && '0' == cp[3]) cp = &cp[4];
|
||
|
else cp = &cp[2];
|
||
|
res = 1.0/0.0;
|
||
|
// TODO: allow "INITY"
|
||
|
}
|
||
|
else if ('.' == c)
|
||
|
{
|
||
|
mant = 0;
|
||
|
goto after_dp;
|
||
|
}
|
||
|
else if (isdigit(c))
|
||
|
{
|
||
|
mant = c - '0'; // mantissa
|
||
|
while (isdigit(c = *cp++))
|
||
|
{
|
||
|
uint8_t d = c - '0';
|
||
|
if (mant <= (UINT64_MAX / 10)) mant *= 10;
|
||
|
else
|
||
|
{
|
||
|
if (0 == d) expt += 1;
|
||
|
else
|
||
|
{
|
||
|
QUODBG(fprintf(stderr, "- overlow in emyg_strtod 1\n"));
|
||
|
goto no_conv;
|
||
|
}
|
||
|
}
|
||
|
if (mant <= (UINT64_MAX - d)) mant += d;
|
||
|
else
|
||
|
{
|
||
|
QUODBG(fprintf(stderr, "- overlow in emyg_strtod 2\n"));
|
||
|
goto no_conv;
|
||
|
}
|
||
|
}
|
||
|
if ('.' == c)
|
||
|
{
|
||
|
after_dp:
|
||
|
while (isdigit(c = *cp++))
|
||
|
{
|
||
|
uint8_t d = c - '0';
|
||
|
if (mant <= (UINT64_MAX / 10)) mant *= 10;
|
||
|
else
|
||
|
{
|
||
|
if (0 == d)
|
||
|
{
|
||
|
/* ignore trailing zeroes by not scaling nor bumping dadp */
|
||
|
continue;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
QUODBG(fprintf(stderr, "- overlow in emyg_strtod 3\n"));
|
||
|
//goto no_conv;
|
||
|
continue; // ignore extra digits
|
||
|
}
|
||
|
}
|
||
|
if (mant <= (UINT64_MAX - d)) mant += d;
|
||
|
else
|
||
|
{
|
||
|
QUODBG(fprintf(stderr, "- overlow in emyg_strtod 4\n"));
|
||
|
//goto no_conv;
|
||
|
continue; // ignore extra digits
|
||
|
}
|
||
|
dadp++;
|
||
|
}
|
||
|
}
|
||
|
if ('e' == c || 'E' == c)
|
||
|
{
|
||
|
c = *cp++;
|
||
|
if ('-' == c) { expm = 1; c = *cp++; }
|
||
|
else if ('+' == c) { c = *cp++; }
|
||
|
else {}
|
||
|
|
||
|
if (isdigit(c))
|
||
|
{
|
||
|
expt += c - '0';
|
||
|
while (isdigit(c = *cp++))
|
||
|
{
|
||
|
uint8_t d = c - '0';
|
||
|
if (expt <= (INT_MAX / 10)) expt *= 10;
|
||
|
else
|
||
|
{
|
||
|
QUODBG(fprintf(stderr, "- overlow in emyg_strtod\n"));
|
||
|
goto no_conv;
|
||
|
}
|
||
|
if (expt <= (INT_MAX - d)) expt += d;
|
||
|
else
|
||
|
{
|
||
|
QUODBG(fprintf(stderr, "- overlow in emyg_strtod\n"));
|
||
|
goto no_conv;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// oops, not an exp at all
|
||
|
c = *--cp;
|
||
|
if (('-' == c) || ('+' == c)) --cp;
|
||
|
res = 0.0;
|
||
|
}
|
||
|
}
|
||
|
if (expm) expt = -expt;
|
||
|
res = atod_guts(mant, expt - dadp);
|
||
|
}
|
||
|
else // no conversion
|
||
|
{
|
||
|
no_conv:
|
||
|
cp = nptr + 1;
|
||
|
res = 0.0;
|
||
|
}
|
||
|
if (NULL != endptr) *endptr = (char *)--cp;
|
||
|
return minus ? -res : res;
|
||
|
}
|
||
|
|
||
|
#ifdef TESTING_QUOREM
|
||
|
|
||
|
int main (int argc, char **argv)
|
||
|
{
|
||
|
if ((argc == 4) && (0 == strcmp(argv[1], "-p")))
|
||
|
{
|
||
|
char *p;
|
||
|
double d = atod_guts(strtoull(argv[2], &p, 10), strtol(argv[3], &p, 10));
|
||
|
printf("%.17g %a\n", d, d);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if ((argc == 3) && (0 == strcmp(argv[1], "-s")))
|
||
|
{
|
||
|
char *p;
|
||
|
double d = emyg_strtod(argv[2], &p);
|
||
|
printf("%.17g %a\n", d, d);
|
||
|
return 0;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
#endif
|