mirror of
https://github.com/janet-lang/janet
synced 2024-11-28 19:19:53 +00:00
Update strtod.c, cleaning up code.
Rename Mant -> BigNat, fix multiply code so we can use 31 bits per digit.
This commit is contained in:
parent
6c8f49206d
commit
bdcd3a3dbf
@ -22,14 +22,11 @@
|
|||||||
|
|
||||||
/* Use a custom double parser instead of libc's strtod for better portability
|
/* Use a custom double parser instead of libc's strtod for better portability
|
||||||
* and control. Also, uses a less strict rounding method than ieee to not incur
|
* and control. Also, uses a less strict rounding method than ieee to not incur
|
||||||
* the cost of 4000 loc and dependence on arbitary precision arithmetic. There
|
* the cost of 4000 loc and dependence on arbitary precision arithmetic. Includes
|
||||||
* is no plan to use arbitrary precision arithmetic for parsing numbers, and a
|
* subset of multiple precision functionality needed for correct rounding.
|
||||||
* formal rounding mode has yet to be chosen (round towards 0 seems
|
|
||||||
* reasonable).
|
|
||||||
*
|
*
|
||||||
* This version has been modified for much greater flexibility in parsing, such
|
* This version has been modified for much greater flexibility in parsing, such
|
||||||
* as choosing the radix, supporting integer output, and returning Janets
|
* as choosing the radix and supporting scientific notation with any radix.
|
||||||
* directly.
|
|
||||||
*
|
*
|
||||||
* Numbers are of the form [-+]R[rR]I.F[eE&][-+]X where R is the radix, I is
|
* Numbers are of the form [-+]R[rR]I.F[eE&][-+]X where R is the radix, I is
|
||||||
* the integer part, F is the fractional part, and X is the exponent. All
|
* the integer part, F is the fractional part, and X is the exponent. All
|
||||||
@ -66,11 +63,11 @@ static uint8_t digit_lookup[128] = {
|
|||||||
25,26,27,28,29,30,31,32,33,34,35,0xff,0xff,0xff,0xff,0xff
|
25,26,27,28,29,30,31,32,33,34,35,0xff,0xff,0xff,0xff,0xff
|
||||||
};
|
};
|
||||||
|
|
||||||
#define MANT_NBIT 27
|
#define BIGNAT_NBIT 31
|
||||||
#define MANT_BASE 0x8000000
|
#define BIGNAT_BASE 0x80000000U
|
||||||
|
|
||||||
/* Allow for BigInt mantissa. Mant is a natural number. */
|
/* Allow for large mantissa. BigNat is a natural number. */
|
||||||
struct Mant {
|
struct BigNat {
|
||||||
uint32_t first_digit; /* First digit so we don't need to allocate when not needed. */
|
uint32_t first_digit; /* First digit so we don't need to allocate when not needed. */
|
||||||
int32_t n; /* n digits */
|
int32_t n; /* n digits */
|
||||||
int32_t cap; /* allocated digit capacity */
|
int32_t cap; /* allocated digit capacity */
|
||||||
@ -78,7 +75,7 @@ struct Mant {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* Allocate n more digits for mant. Return a pointer to these digits. */
|
/* Allocate n more digits for mant. Return a pointer to these digits. */
|
||||||
static uint32_t *mant_extra(struct Mant *mant, int32_t n) {
|
static uint32_t *bignat_extra(struct BigNat *mant, int32_t n) {
|
||||||
int32_t oldn = mant->n;
|
int32_t oldn = mant->n;
|
||||||
int32_t newn = oldn + n;
|
int32_t newn = oldn + n;
|
||||||
if (mant->cap < newn) {
|
if (mant->cap < newn) {
|
||||||
@ -95,75 +92,70 @@ static uint32_t *mant_extra(struct Mant *mant, int32_t n) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Append a digit */
|
/* Append a digit */
|
||||||
static void mant_append(struct Mant *mant, uint32_t dig) {
|
static void bignat_append(struct BigNat *mant, uint32_t dig) {
|
||||||
mant_extra(mant, 1)[0] = dig;
|
bignat_extra(mant, 1)[0] = dig;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Add term to mant */
|
/* Add term to mant */
|
||||||
static void mant_add(struct Mant *mant, uint32_t dig) {
|
static void bignat_add(struct BigNat *mant, uint32_t dig) {
|
||||||
int32_t i;
|
int32_t i;
|
||||||
int carry = 0;
|
int carry = 0;
|
||||||
uint32_t next = mant->first_digit + dig;
|
uint32_t next = mant->first_digit + dig;
|
||||||
if (next >= MANT_BASE) {
|
if (next >= BIGNAT_BASE) {
|
||||||
next -= MANT_BASE;
|
next -= BIGNAT_BASE;
|
||||||
carry = 1;
|
carry = 1;
|
||||||
}
|
}
|
||||||
mant->first_digit = next;
|
mant->first_digit = next;
|
||||||
for (i = 0; i < mant->n; i++) {
|
for (i = 0; i < mant->n; i++) {
|
||||||
if (!carry) return;
|
if (!carry) return;
|
||||||
uint32_t next = mant->digits[i] + 1;
|
uint32_t next = mant->digits[i] + 1;
|
||||||
if (next >= MANT_BASE) {
|
if (next >= BIGNAT_BASE) {
|
||||||
next = 0;
|
next = 0;
|
||||||
} else {
|
} else {
|
||||||
carry = 0;
|
carry = 0;
|
||||||
}
|
}
|
||||||
mant->digits[i] = next;
|
mant->digits[i] = next;
|
||||||
}
|
}
|
||||||
if (carry) mant_append(mant, 1);
|
if (carry) bignat_append(mant, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Multiply the mantissa mant by a factor */
|
/* Multiply the mantissa mant by a factor */
|
||||||
static void mant_mul(struct Mant *mant, uint32_t factor) {
|
static void bignat_mul(struct BigNat *mant, uint32_t factor) {
|
||||||
int32_t i;
|
int32_t i;
|
||||||
uint64_t carry = mant->first_digit * factor;
|
uint64_t carry = ((uint64_t) mant->first_digit) * factor;
|
||||||
mant->first_digit = carry % MANT_BASE;
|
mant->first_digit = carry % BIGNAT_BASE;
|
||||||
carry /= MANT_BASE;
|
carry /= BIGNAT_BASE;
|
||||||
for (i = 0; i < mant->n; i++) {
|
for (i = 0; i < mant->n; i++) {
|
||||||
carry += mant->digits[i] * factor;
|
carry += ((uint64_t) mant->digits[i]) * factor;
|
||||||
mant->digits[i] = carry % MANT_BASE;
|
mant->digits[i] = carry % BIGNAT_BASE;
|
||||||
carry /= MANT_BASE;
|
carry /= BIGNAT_BASE;
|
||||||
}
|
}
|
||||||
if (carry) mant_append(mant, carry);
|
if (carry) bignat_append(mant, carry);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Divide the mantissa mant by a factor. Returns the remainder */
|
/* Divide the mantissa mant by a factor. Drop the remainder. */
|
||||||
static int32_t mant_div(struct Mant *mant, uint32_t divisor) {
|
static void bignat_div(struct BigNat *mant, uint32_t divisor) {
|
||||||
int32_t i;
|
int32_t i;
|
||||||
uint32_t quotient, remainder;
|
uint32_t quotient, remainder;
|
||||||
uint64_t dividend;
|
uint64_t dividend;
|
||||||
remainder = 0;
|
remainder = 0;
|
||||||
for (i = mant->n - 1; i >= 0; i--) {
|
for (i = mant->n - 1; i >= 0; i--) {
|
||||||
dividend = ((uint64_t)remainder * MANT_BASE) + mant->digits[i];
|
dividend = ((uint64_t)remainder * BIGNAT_BASE) + mant->digits[i];
|
||||||
if (i < mant->n - 1) mant->digits[i + 1] = quotient;
|
if (i < mant->n - 1) mant->digits[i + 1] = quotient;
|
||||||
quotient = dividend / divisor;
|
quotient = dividend / divisor;
|
||||||
remainder = dividend % divisor;
|
remainder = dividend % divisor;
|
||||||
mant->digits[i] = remainder;
|
mant->digits[i] = remainder;
|
||||||
}
|
}
|
||||||
dividend = ((uint64_t)remainder * MANT_BASE) + mant->first_digit;
|
dividend = ((uint64_t)remainder * BIGNAT_BASE) + mant->first_digit;
|
||||||
if (mant->n) {
|
if (mant->n && mant->digits[mant->n - 1] == 0) mant->n--;
|
||||||
if (mant->digits[mant->n - 1] == 0) mant->n--;
|
mant->first_digit = dividend / divisor;
|
||||||
}
|
|
||||||
quotient = dividend / divisor;
|
|
||||||
remainder = dividend % divisor;
|
|
||||||
mant->first_digit = quotient;
|
|
||||||
return remainder;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Shift left by a multiple of MANT_NBIT */
|
/* Shift left by a multiple of BIGNAT_NBIT */
|
||||||
static void mant_lshift_n(struct Mant *mant, int n) {
|
static void bignat_lshift_n(struct BigNat *mant, int n) {
|
||||||
if (!n) return;
|
if (!n) return;
|
||||||
int32_t oldn = mant->n;
|
int32_t oldn = mant->n;
|
||||||
mant_extra(mant, n);
|
bignat_extra(mant, n);
|
||||||
memmove(mant->digits + n, mant->digits, sizeof(uint32_t) * oldn);
|
memmove(mant->digits + n, mant->digits, sizeof(uint32_t) * oldn);
|
||||||
memset(mant->digits, 0, sizeof(uint32_t) * (n - 1));
|
memset(mant->digits, 0, sizeof(uint32_t) * (n - 1));
|
||||||
mant->digits[n - 1] = mant->first_digit;
|
mant->digits[n - 1] = mant->first_digit;
|
||||||
@ -185,11 +177,11 @@ static int clz(uint32_t x) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Extract double value from mantissa */
|
/* Extract double value from mantissa */
|
||||||
static double mant_extract(struct Mant *mant, int32_t exponent2) {
|
static double bignat_extract(struct BigNat *mant, int32_t exponent2) {
|
||||||
uint64_t top53;
|
uint64_t top53;
|
||||||
int32_t n = mant->n;
|
int32_t n = mant->n;
|
||||||
/* Get most significant 52 bits from mant. Bit52 (0 indexed) should
|
/* Get most significant 53 bits from mant. Bit52 (0 indexed) should
|
||||||
* always be 1. */
|
* always be 1. This is essentially a large right shift on mant.*/
|
||||||
if (n) {
|
if (n) {
|
||||||
/* Two or more digits */
|
/* Two or more digits */
|
||||||
uint64_t d1 = mant->digits[n - 1]; /* MSD (non-zero) */
|
uint64_t d1 = mant->digits[n - 1]; /* MSD (non-zero) */
|
||||||
@ -197,16 +189,19 @@ static double mant_extract(struct Mant *mant, int32_t exponent2) {
|
|||||||
uint64_t d3 = (n > 2) ? mant->digits[n - 3] : (n == 2) ? mant->first_digit : 0;
|
uint64_t d3 = (n > 2) ? mant->digits[n - 3] : (n == 2) ? mant->first_digit : 0;
|
||||||
int lz = clz(d1);
|
int lz = clz(d1);
|
||||||
int nbits = 32 - lz;
|
int nbits = 32 - lz;
|
||||||
top53 = (d2 << (54 - MANT_NBIT)) + (d3 >> (2 * MANT_NBIT - 54));
|
/* First get 54 bits */
|
||||||
|
top53 = (d2 << (54 - BIGNAT_NBIT)) + (d3 >> (2 * BIGNAT_NBIT - 54));
|
||||||
top53 >>= nbits;
|
top53 >>= nbits;
|
||||||
top53 |= (d1 << (54 - nbits));
|
top53 |= (d1 << (54 - nbits));
|
||||||
|
/* Rounding based on lowest bit of 54 */
|
||||||
if (top53 & 1) top53++;
|
if (top53 & 1) top53++;
|
||||||
top53 >>= 1;
|
top53 >>= 1;
|
||||||
if (top53 > 0x1FffffFFFFffffUL) {
|
if (top53 > 0x1FffffFFFFffffUL) {
|
||||||
top53 >>= 1;
|
top53 >>= 1;
|
||||||
exponent2++;
|
exponent2++;
|
||||||
}
|
}
|
||||||
exponent2 += (nbits - 53) + MANT_NBIT * n;
|
/* Correct exponent - to correct for large right shift to mantissa. */
|
||||||
|
exponent2 += (nbits - 53) + BIGNAT_NBIT * n;
|
||||||
} else {
|
} else {
|
||||||
/* One digit */
|
/* One digit */
|
||||||
top53 = mant->first_digit;
|
top53 = mant->first_digit;
|
||||||
@ -219,7 +214,7 @@ static double mant_extract(struct Mant *mant, int32_t exponent2) {
|
|||||||
* denormalized numbers. (When the exponent values are too large) */
|
* denormalized numbers. (When the exponent values are too large) */
|
||||||
static double convert(
|
static double convert(
|
||||||
int negative,
|
int negative,
|
||||||
struct Mant *mant,
|
struct BigNat *mant,
|
||||||
int32_t base,
|
int32_t base,
|
||||||
int32_t exponent) {
|
int32_t exponent) {
|
||||||
|
|
||||||
@ -236,24 +231,25 @@ static double convert(
|
|||||||
|
|
||||||
/* Positive exponents are simple */
|
/* Positive exponents are simple */
|
||||||
while (exponent > 0) {
|
while (exponent > 0) {
|
||||||
mant_mul(mant, base);
|
bignat_mul(mant, base);
|
||||||
exponent--;
|
exponent--;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Negative exponents are tricky - we don't want to loose bits
|
/* Negative exponents are tricky - we don't want to loose bits
|
||||||
* from integer division, so we need to premultiply. */
|
* from integer division, so we need to premultiply. */
|
||||||
if (exponent < 0) {
|
if (exponent < 0) {
|
||||||
mant_lshift_n(mant, 20 - exponent);
|
int32_t shamt = 5 - exponent / 4;
|
||||||
exponent2 -= (20 - exponent) * MANT_NBIT;
|
bignat_lshift_n(mant, shamt);
|
||||||
|
exponent2 -= shamt * BIGNAT_NBIT;
|
||||||
while (exponent < 0) {
|
while (exponent < 0) {
|
||||||
mant_div(mant, base);
|
bignat_div(mant, base);
|
||||||
exponent++;
|
exponent++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return negative
|
return negative
|
||||||
? -mant_extract(mant, exponent2)
|
? -bignat_extract(mant, exponent2)
|
||||||
: mant_extract(mant, exponent2);
|
: bignat_extract(mant, exponent2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Scan a real (double) from a string. If the string cannot be converted into
|
/* Scan a real (double) from a string. If the string cannot be converted into
|
||||||
@ -264,7 +260,7 @@ double janet_scan_number(
|
|||||||
int *err) {
|
int *err) {
|
||||||
const uint8_t *end = str + len;
|
const uint8_t *end = str + len;
|
||||||
int seenadigit = 0;
|
int seenadigit = 0;
|
||||||
struct Mant mant = {0};
|
struct BigNat mant = {0};
|
||||||
int ex = 0;
|
int ex = 0;
|
||||||
int base = 10;
|
int base = 10;
|
||||||
int seenpoint = 0;
|
int seenpoint = 0;
|
||||||
@ -327,8 +323,8 @@ double janet_scan_number(
|
|||||||
int digit = digit_lookup[*str & 0x7F];
|
int digit = digit_lookup[*str & 0x7F];
|
||||||
if (*str > 127 || digit >= base) goto error;
|
if (*str > 127 || digit >= base) goto error;
|
||||||
if (seenpoint) ex--;
|
if (seenpoint) ex--;
|
||||||
mant_mul(&mant, base);
|
bignat_mul(&mant, base);
|
||||||
mant_add(&mant, digit);
|
bignat_add(&mant, digit);
|
||||||
seenadigit = 1;
|
seenadigit = 1;
|
||||||
}
|
}
|
||||||
str++;
|
str++;
|
||||||
|
Loading…
Reference in New Issue
Block a user