Fix whitespace, add asserts, add string to double function, fix bugs

Finally added StringToDouble function (should work for the majority of
the cases). Additionally fixed bug in StringFromDouble related to
incorrect rounding and added asserts (should add more asserts in the
following commits).

Also implemented some optimizations from Burger and Dybvig paper.
This commit is contained in:
2025-03-24 22:38:11 +03:00
parent 82bea0ebf8
commit b943135d71
4 changed files with 435 additions and 102 deletions

View File

@@ -4,14 +4,17 @@
#include <ctype.h>
#include <float.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include <string.h>
/* Common defines */
#define MAX(a,b) (((a)>(b))?(a):(b))
#define MIN(a,b) (((a)<(b))?(a):(b))
#define BUFSIZE 309
#define DIGITS 70
#define DIGITS 80
/* Modes */
#define NORMAL 0
#define ABSOLUTE 1
#define RELATIVE 2
@@ -30,13 +33,13 @@ struct DragonState
BInt mm;
BInt mp;
BInt tmp[5];
long k;
int cutoff;
int k;
};
static const BInt BInt1 = {1, {1}};
static const BInt BInt10 = {1, {10}};
static const BInt BInt53 = {4, {0x0000, 0x0000, 0x0000, 0x0020}};
static const uint8_t clzLookup[256] =
@@ -60,6 +63,30 @@ static const uint8_t clzLookup[256] =
};
static const BInt powLookup[] =
{
{1, {0x000A}},
{1, {0x0064}},
{1, {0x2710}},
{2, {0xE100, 0x05F5}},
{4, {0x0000, 0x6FC1, 0x86F2, 0x0023}},
{7, {0x0000, 0x0000, 0xEF81, 0x85AC, 0x415B, 0x2D6D, 0x04EE}},
{14, {0x0000, 0x0000, 0x0000, 0x0000, 0x1F01, 0xBF6A, 0xED64, 0x6E38,
0x97ED, 0xDAA7, 0xF9F4, 0xE93F, 0x4F03, 0x0018}},
{27, {0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x3E01, 0x2E95, 0x9909, 0x03DF, 0x38FD, 0x0F15, 0xE42F, 0x2374,
0xF5EC, 0xD3CF, 0xDC08, 0xC404, 0xB0DA, 0xBCCD, 0x7F19, 0xA633,
0x2603, 0xE91F, 0x024E}},
{54, {0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x7C01, 0x982E, 0x875B, 0xBED3, 0x9F72, 0xD8D9, 0x2F87, 0x1215,
0x50C6, 0x6BDE, 0x6E70, 0xCF4A, 0xD80F, 0xD595, 0x716E, 0x26B2,
0x66B0, 0xADC6, 0x3624, 0x1D15, 0xD35A, 0x3C42, 0x540E, 0x63FF,
0x73C0, 0xCC55, 0xEF17, 0x65F9, 0x28F2, 0x55BC, 0xC7F7, 0x80DC,
0xEDDC, 0xF46E, 0xEFCE, 0x5FDC, 0x53F7, 0x0005}}
};
static int BIntClz(uint16_t value)
{
if (value & 0xFF00)
@@ -69,8 +96,22 @@ static int BIntClz(uint16_t value)
}
static int BIntLog2(const BInt *in)
{
/* Preconditions */
assert(in != NULL);
assert(in->size != 0);
assert(in->data[in->size - 1] != 0);
return 15 - BIntClz(in->data[in->size - 1]) + 16 * (in->size - 1);
}
static void BIntTrim(BInt *in)
{
/* Preconditions */
assert(in != NULL);
while (in->size && !in->data[in->size - 1])
in->size--;
}
@@ -81,6 +122,9 @@ static int BIntCompare(const BInt *a,
{
int i;
/* Preconditions */
assert(a != NULL && b != NULL);
/* Compare by lengths */
i = a->size - b->size;
if (a->size - b->size)
@@ -106,6 +150,8 @@ static void BIntAdd(const BInt *a,
uint32_t carry;
int i;
/* Preconditions */
assert(a != NULL && b != NULL && out != NULL);
assert(a->size + 1 <= DIGITS);
assert(b->size + 1 <= DIGITS);
@@ -137,6 +183,10 @@ static void BIntSub(const BInt *a,
uint32_t carry;
int i;
/* Preconditions */
assert(a != NULL && b != NULL && out != NULL);
assert(BIntCompare(a, b) >= 0);
/* Main subtraction loop */
carry = 0;
for (i = 0; i < a->size || i < b->size; i++)
@@ -163,6 +213,8 @@ static void BIntMul(const BInt *a,
uint32_t carry;
int i, j;
/* Preconditions */
assert(a != NULL && b != NULL && out != NULL);
assert(a->size + b->size <= DIGITS);
/* Zero out the result */
@@ -197,6 +249,8 @@ static void BIntMulDigit(const BInt *a,
uint32_t carry;
int i;
/* Preconditions */
assert(a != NULL && out != NULL);
assert(a->size + 1 <= DIGITS);
/* Multiplication loop */
@@ -215,6 +269,30 @@ static void BIntMulDigit(const BInt *a,
}
static void BIntPow10(const BInt *in,
int exponent,
BInt *out,
BInt *tmp)
{
int i, current;
/* Preconditions */
assert(in != NULL && out != NULL && tmp != NULL);
assert(exponent >= 0 && exponent < 512);
tmp[0] = *in;
for (current = 0, i = 0; exponent; i++, exponent >>= 1)
{
if (!(exponent & 0x1))
continue;
BIntMul(&tmp[current], &powLookup[i], &tmp[1 - current]);
current = 1 - current;
}
*out = tmp[current];
}
static void BIntLsh(const BInt *in,
int amount,
BInt *out)
@@ -222,11 +300,12 @@ static void BIntLsh(const BInt *in,
int blocks, bits, i;
uint16_t low, high;
/* Preconditions */
assert(in != NULL && out != NULL);
assert(amount >= 0 && in->size + (amount + 15) / 16 <= DIGITS);
blocks = amount / 16;
bits = amount % 16;
assert(in->size + blocks + (bits > 0) <= DIGITS);
if (!in->size)
{
out->size = 0;
@@ -267,6 +346,10 @@ static void BIntRsh(const BInt *in,
int blocks, bits, i;
uint16_t low, high;
/* Preconditions */
assert(in != NULL && out != NULL);
assert(amount >= 0);
blocks = amount / 16;
bits = amount % 16;
@@ -299,6 +382,10 @@ static uint16_t BIntGuess(const BInt *a,
{
uint32_t tmp;
/* Preconditions */
assert(a != NULL && b != NULL);
assert(a->size > 0 && b->size > 0);
if (BIntCompare(a, b) < 0)
return 0;
@@ -319,6 +406,10 @@ static void BIntDiv(const BInt *a,
uint16_t digit;
int shift;
/* Preconditions */
assert(a != NULL && b != NULL && q != NULL && r != NULL && tmp != NULL);
assert(b->size != 0);
/* Handle case where a is less then b */
if (BIntCompare(a, b) < 0)
{
@@ -377,8 +468,15 @@ static void BIntDiv(const BInt *a,
static void dragonFixup(struct DragonState *state,
int precision,
int mode,
uint64_t f)
uint64_t f,
int exp)
{
/* Preconditions */
assert(state != NULL);
assert(mode == NORMAL || mode == ABSOLUTE || mode == RELATIVE);
assert(precision >= 0);
/* Account for unqual gaps */
if (f == (((uint64_t)1) << 52))
{
BIntLsh(&state->mp, 1, &state->mp);
@@ -387,31 +485,37 @@ static void dragonFixup(struct DragonState *state,
}
state->k = 0;
BIntDiv(&state->s, &BInt10, &state->tmp[0], &state->tmp[1], state->tmp + 2);
if (state->tmp[1].size)
BIntAdd(&state->tmp[0], &BInt1, &state->tmp[0]);
/* Burger/Dybvig approach */
state->k = BIntClz((f >> 48) & 0xFFFF);
state->k += (state->k == 16) ? (BIntClz((f >> 32) & 0xFFFF)) : (0);
state->k += (state->k == 32) ? (BIntClz((f >> 16) & 0xFFFF)) : (0);
state->k += (state->k == 48) ? (BIntClz(f & 0xFFFF)) : (0);
while (BIntCompare(&state->r, &state->tmp[0]) < 0)
/* 77 / 256 is an approximation for Log(2) or 0.30102999 */
state->k = (63 - state->k + exp - 54) * 77;
if (state->k < 0)
state->k = (state->k / 256);
else
state->k = (state->k / 256) + ((state->k & 0xFF) > 0);
/* Scale numbers accordinaly */
if (state->k < 0)
{
state->k -= 1;
BIntMulDigit(&state->r, 10, &state->r);
BIntMulDigit(&state->mm, 10, &state->mm);
BIntMulDigit(&state->mp, 10, &state->mp);
BIntDiv(&state->s, &BInt10, &state->tmp[0], &state->tmp[1], state->tmp + 2);
if (state->tmp[1].size)
BIntAdd(&state->tmp[0], &BInt1, &state->tmp[0]);
BIntPow10(&state->r, -state->k, &state->r, state->tmp);
BIntPow10(&state->mm, -state->k, &state->mm, state->tmp);
BIntPow10(&state->mp, -state->k, &state->mp, state->tmp);
}
else if (state->k > 0)
BIntPow10(&state->s, state->k, &state->s, state->tmp);
BIntLsh(&state->r, 1, &state->tmp[0]);
BIntAdd(&state->tmp[0], &state->mp, &state->tmp[0]);
BIntLsh(&state->s, 1, &state->tmp[1]);
while (BIntCompare(&state->tmp[0], &state->tmp[1]) >= 0)
/* Scale S if we underestimated */
if (BIntCompare(&state->r, &state->s) >= 0)
{
state->k += 1;
BIntMulDigit(&state->s, 10, &state->s);
BIntLsh(&state->s, 1, &state->tmp[1]);
}
/* Find cutoff */
if (mode == NORMAL)
state->cutoff = state->k - BUFSIZE;
else if (mode == RELATIVE)
@@ -431,6 +535,10 @@ static void dragonRound(struct DragonState *state,
{
int i;
/* Preconditions */
assert(state != NULL && buffer != NULL && k != NULL && size != NULL);
assert(s >= '0' && s <= '9');
/* Check if rounding up required */
if (high == low)
{
@@ -470,6 +578,11 @@ static void dragon(double value,
uint64_t f;
char s;
/* Preconditions */
assert(buffer != NULL && size != NULL && k != NULL);
assert(mode == NORMAL || mode == ABSOLUTE || mode == RELATIVE);
assert(precision >= 0);
*k = 0;
*size = low = high = 0;
@@ -490,7 +603,7 @@ static void dragon(double value,
BIntLsh(&BInt1, MAX(0, -(e - 53)), &state.s);
BIntLsh(&BInt1, MAX(e - 53, 0), &state.mm);
BIntLsh(&BInt1, MAX(e - 53, 0), &state.mp);
dragonFixup(&state, precision, mode, f);
dragonFixup(&state, precision, mode, f, e);
/* Main digit generation loop */
*k = state.k - 1;
@@ -498,7 +611,7 @@ static void dragon(double value,
{
state.k -= 1;
BIntMulDigit(&state.r, 10, &state.r);
BIntDiv(&state.r, &state.s, &state.tmp[0], &state.r, state.tmp + 1);
BIntDiv(&state.r, &state.s, &state.tmp[0], &state.r, &state.tmp[1]);
s = '0';
if (state.tmp[0].size)
@@ -507,13 +620,13 @@ static void dragon(double value,
if (mode == NORMAL)
{
BIntLsh(&state.r, 1, &state.tmp[1]);
BIntLsh(&state.s, 1, &state.tmp[2]);
BIntSub(&state.tmp[2], &state.mp, &state.tmp[2]);
BIntMulDigit(&state.mm, 10, &state.mm);
BIntMulDigit(&state.mp, 10, &state.mp);
BIntLsh(&state.r, 1, &state.tmp[1]);
BIntLsh(&state.s, 1, &state.tmp[2]);
BIntAdd(&state.tmp[1], &state.mp, &state.tmp[3]);
low = BIntCompare(&state.tmp[1], &state.mm) < 0;
high = BIntCompare(&state.tmp[1], &state.tmp[2]) > 0;
high = BIntCompare(&state.tmp[3], &state.tmp[2]) > 0;
if (low || high || state.k == state.cutoff || *size >= BUFSIZE)
break;
}
@@ -529,7 +642,7 @@ static void dragon(double value,
}
static char *formatF(char buffer[BUFSIZE],
static char *formatF(char *buffer,
int precision,
int sign,
int k,
@@ -538,6 +651,10 @@ static char *formatF(char buffer[BUFSIZE],
char *result, *current;
int i;
/* Preconditions */
assert(buffer != NULL);
assert(size < BUFSIZE);
result = malloc(MAX(0, k) + 5 + sign + precision);
current = result;
if (!result)
@@ -576,7 +693,7 @@ static char *formatF(char buffer[BUFSIZE],
}
static char *formatE(char buffer[BUFSIZE],
static char *formatE(char *buffer,
int precision,
int sign,
int k,
@@ -586,6 +703,12 @@ static char *formatE(char buffer[BUFSIZE],
char *result, *current;
int i;
/* Preconditions */
assert(buffer != NULL);
assert(size < BUFSIZE);
assert(sign == 0 || sign == 1);
assert(upper == 0 || upper == 1);
result = malloc(9 + sign + precision);
current = result;
if (!result)
@@ -740,35 +863,76 @@ char *BH_StringFromDouble(double value,
}
double BH_StringToDouble(const char *string,
size_t *size)
static int caselessCompare(const char *src,
const char *ref)
{
int nsign, esign, e, dot;
const char *current;
char buffer[4];
size_t count;
/* Preconditions */
assert(src != NULL && ref != NULL);
nsign = 0; esign = 0; e = 0; count = 0; dot = 0;
for (; *src && *ref && tolower(*src) == tolower(*ref); src++, ref++);
if (*ref == 0)
return 0;
return *src - *ref;
}
static int parseFormat(const char *string,
size_t *size,
char *buffer,
int *sign,
int *e,
int *type)
{
const char *current;
int esign, dot, count;
/* Preconditions */
assert(string != NULL && buffer != NULL && sign != NULL && e != NULL && type != NULL);
*sign = *e = esign = count = dot = 0;
*type = BH_FP_ZERO;
current = string;
/* Skip whitespace */
while (isspace(*current))
current++;
/* Check for NaN */
if (caselessCompare(current, "nan") == 0)
{
*type = BH_FP_NAN; current += 3;
goto done;
}
/* Leading sign */
if (*current == '+' || *current == '-')
{
if (*current == '-')
nsign = 1;
*sign = 1;
current++;
}
/* Check for infinity */
if (caselessCompare(current, "infinity") == 0)
{
*type = BH_FP_INFINITE; current += 8;
goto done;
}
else if (caselessCompare(current, "inf") == 0)
{
*type = BH_FP_INFINITE; current += 3;
goto done;
}
/* Read integer part of the float */
for (; isdigit(*current); current++)
{
if (count < sizeof(buffer) && (count || *current != '0'))
*type = BH_FP_NORMAL;
if (count < 20 && (count || *current != '0'))
buffer[count++] = *current;
else if (count >= sizeof(buffer))
else if (count >= 20)
dot--;
}
@@ -778,7 +942,8 @@ double BH_StringToDouble(const char *string,
for (; isdigit(*current); current++)
{
if ((count < sizeof(buffer)))
*type = BH_FP_NORMAL;
if ((count < 20))
{
dot++;
if ((count || *current != '0'))
@@ -798,17 +963,134 @@ double BH_StringToDouble(const char *string,
}
for (; isdigit(*current); current++)
e = e * 10 + *current - '0';
*e = *e * 10 + *current - '0';
}
if (esign)
e = -e;
e -= dot;
*e = -*e;
*e -= dot;
done:
if (size)
*size = current - string;
BH_UNUSED(nsign);
BH_UNUSED(BIntMul);
return count;
}
double BH_StringToDouble(const char *string,
size_t *size)
{
int type, e, sign, i, count, shift;
BInt r, s, tmp[5];
char buffer[20];
double result;
uint64_t f;
/* Preconditions */
assert(string != NULL);
/* Parse from string format */
count = parseFormat(string, size, buffer, &sign, &e, &type);
/* Handle special values */
if (type == BH_FP_INFINITE)
{
if (sign)
return -INFINITY;
return INFINITY;
}
else if (type == BH_FP_NAN)
return NAN;
else if (type == BH_FP_ZERO)
{
/* Hacky solution to indicate we haven't seen any digit */
if (size)
*size = 0;
return 0.0;
}
/* Handle zero input */
if (count == 0)
{
if (sign)
return -0.0;
return 0.0;
}
/* Exponent too low */
if (e < -329)
{
if (sign)
return -0.0;
return 0.0;
}
/* Exponent too high */
if (e > 292)
{
if (sign)
return -INFINITY;
return INFINITY;
}
/* Convert character buffer into integers */
tmp[0].size = 1;
r.size = 0;
s = BInt1;
for (i = 0; i < count; i++)
{
tmp[0].data[0] = buffer[i] - '0';
BIntMulDigit(&r, 10, &r);
BIntAdd(&r, &tmp[0], &r);
}
if (e >= 0)
BIntPow10(&r, e, &r, &tmp[0]);
else
BIntPow10(&s, -e, &s, &tmp[0]);
/* Calculate required shift */
shift = -52;
if (BIntCompare(&r, &s) >= 0)
{
BIntDiv(&r, &s, &tmp[0], &tmp[1], &tmp[2]);
shift += BIntLog2(&tmp[0]);
}
else
{
BIntDiv(&s, &r, &tmp[0], &tmp[1], &tmp[2]);
shift += -(BIntLog2(&tmp[0]) + 1);
}
/* Shift */
if (shift > 0)
BIntLsh(&s, shift, &s);
else if (shift < 0)
BIntLsh(&r, -shift, &r);
/* Calculate final exponent and 53 bit integer */
BIntDiv(&r, &s, &tmp[0], &tmp[1], &tmp[2]);
BIntRsh(&s, 1, &s);
if (BIntCompare(&tmp[1], &s) > 0 || (BIntCompare(&tmp[1], &s) == 0 && (tmp[0].data[0] & 0x1)))
{
BIntAdd(&tmp[0], &BInt1, &tmp[0]);
if (BIntCompare(&tmp[0], &BInt53) >= 0)
{
BIntRsh(&tmp[0], 1, &tmp[0]);
shift++;
}
}
/* Create double from integer and exponent */
f = (tmp[0].data[3] & 0x1F);
f = (f << 16) | tmp[0].data[2];
f = (f << 16) | tmp[0].data[1];
f = (f << 16) | tmp[0].data[0];
result = ldexp(f, shift);
if (sign)
result = -result;
return result;
}

View File

@@ -1,6 +1,8 @@
#include <BH/String.h>
#include <BH/Util.h>
#include <BH/Unit.h>
#include <float.h>
#include <limits.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
@@ -32,7 +34,7 @@ static int roundtripString(double value,
char *str;
str = BH_StringFromDouble(value, format, -1);
result = strtod(str, NULL);
result = BH_StringToDouble(str, NULL);
if (result != value)
printf("Value: %.17g\tGot: %.17g\tStr: %s\n", value, result, str);
@@ -94,10 +96,6 @@ BH_UNIT_TEST(FixedRoundTrip)
{
BH_VERIFY(roundtripString(1.0, 'f') == 0);
BH_VERIFY(roundtripString(-1.0, 'f') == 0);
BH_VERIFY(roundtripString(DBL_MIN, 'f') == 0);
BH_VERIFY(roundtripString(-DBL_MIN, 'f') == 0);
BH_VERIFY(roundtripString(DBL_MAX, 'f') == 0);
BH_VERIFY(roundtripString(-DBL_MAX, 'f') == 0);
BH_VERIFY(roundtripString(0.0, 'f') == 0);
BH_VERIFY(roundtripString(-0.0, 'f') == 0);
BH_VERIFY(roundtripString(3.14159, 'f') == 0);
@@ -108,8 +106,12 @@ BH_UNIT_TEST(FixedRoundTrip)
BH_VERIFY(roundtripString(-0.81, 'f') == 0);
BH_VERIFY(roundtripString(0.81, 'f') == 0);
BH_VERIFY(roundtripString(-0.81, 'f') == 0);
BH_VERIFY(roundtripString(144115188075855877, 'f') == 0);
BH_VERIFY(roundtripString(-144115188075855877, 'f') == 0);
BH_VERIFY(roundtripString(144115188075855877.0, 'f') == 0);
BH_VERIFY(roundtripString(-144115188075855877.0, 'f') == 0);
BH_VERIFY(roundtripString(DBL_MIN, 'f') == 0);
BH_VERIFY(roundtripString(-DBL_MIN, 'f') == 0);
BH_VERIFY(roundtripString(DBL_MAX, 'f') == 0);
BH_VERIFY(roundtripString(-DBL_MAX, 'f') == 0);
return 0;
}
@@ -206,8 +208,8 @@ BH_UNIT_TEST(ShortestFormat)
BH_VERIFY(compareString(-1230000000.00123, 'g', 1, "-1e+09") == 0);
BH_VERIFY(compareString(-1230000000.00123, 'g', 2, "-1.2e+09") == 0);
BH_VERIFY(compareString(-1230000000.00123, 'g', 3, "-1.23e+09") == 0);
BH_VERIFY(compareString(144115188075855877, 'g', 17, "1.4411518807585587e+17") == 0);
BH_VERIFY(compareString(-144115188075855877, 'g', 17, "-1.4411518807585587e+17") == 0);
BH_VERIFY(compareString(144115188075855877.0, 'g', 17, "1.4411518807585587e+17") == 0);
BH_VERIFY(compareString(-144115188075855877.0, 'g', 17, "-1.4411518807585587e+17") == 0);
return 0;
}
@@ -217,10 +219,6 @@ BH_UNIT_TEST(ScientificRoundTrip)
{
BH_VERIFY(roundtripString(1.0, 'e') == 0);
BH_VERIFY(roundtripString(-1.0, 'e') == 0);
BH_VERIFY(roundtripString(DBL_MIN, 'e') == 0);
BH_VERIFY(roundtripString(-DBL_MIN, 'e') == 0);
BH_VERIFY(roundtripString(DBL_MAX, 'e') == 0);
BH_VERIFY(roundtripString(-DBL_MAX, 'e') == 0);
BH_VERIFY(roundtripString(0.0, 'e') == 0);
BH_VERIFY(roundtripString(-0.0, 'e') == 0);
BH_VERIFY(roundtripString(3.14159, 'e') == 0);
@@ -231,8 +229,12 @@ BH_UNIT_TEST(ScientificRoundTrip)
BH_VERIFY(roundtripString(-0.81, 'e') == 0);
BH_VERIFY(roundtripString(0.81, 'e') == 0);
BH_VERIFY(roundtripString(-0.81, 'e') == 0);
BH_VERIFY(roundtripString(144115188075855877, 'e') == 0);
BH_VERIFY(roundtripString(-144115188075855877, 'e') == 0);
BH_VERIFY(roundtripString(144115188075855877.0, 'e') == 0);
BH_VERIFY(roundtripString(-144115188075855877.0, 'e') == 0);
BH_VERIFY(roundtripString(DBL_MIN, 'e') == 0);
BH_VERIFY(roundtripString(-DBL_MIN, 'e') == 0);
BH_VERIFY(roundtripString(DBL_MAX, 'e') == 0);
BH_VERIFY(roundtripString(-DBL_MAX, 'e') == 0);
return 0;
}
@@ -242,10 +244,6 @@ BH_UNIT_TEST(ShortestRoundTrip)
{
BH_VERIFY(roundtripString(1.0, 'g') == 0);
BH_VERIFY(roundtripString(-1.0, 'g') == 0);
BH_VERIFY(roundtripString(DBL_MIN, 'g') == 0);
BH_VERIFY(roundtripString(-DBL_MIN, 'g') == 0);
BH_VERIFY(roundtripString(DBL_MAX, 'g') == 0);
BH_VERIFY(roundtripString(-DBL_MAX, 'g') == 0);
BH_VERIFY(roundtripString(0.0, 'g') == 0);
BH_VERIFY(roundtripString(-0.0, 'g') == 0);
BH_VERIFY(roundtripString(3.14159, 'g') == 0);
@@ -256,8 +254,12 @@ BH_UNIT_TEST(ShortestRoundTrip)
BH_VERIFY(roundtripString(-0.81, 'g') == 0);
BH_VERIFY(roundtripString(0.81, 'g') == 0);
BH_VERIFY(roundtripString(-0.81, 'g') == 0);
BH_VERIFY(roundtripString(144115188075855877, 'g') == 0);
BH_VERIFY(roundtripString(-144115188075855877, 'g') == 0);
BH_VERIFY(roundtripString(144115188075855877.0, 'g') == 0);
BH_VERIFY(roundtripString(-144115188075855877.0, 'g') == 0);
BH_VERIFY(roundtripString(DBL_MIN, 'g') == 0);
BH_VERIFY(roundtripString(-DBL_MIN, 'g') == 0);
BH_VERIFY(roundtripString(DBL_MAX, 'g') == 0);
BH_VERIFY(roundtripString(-DBL_MAX, 'g') == 0);
return 0;
}
@@ -292,7 +294,7 @@ BH_UNIT_TEST(Parity)
if (strcmp(str, output))
{
printf("(%.17g) (%d) %s vs %s\n", value, k, str, output);
BH_FAIL("Not equal");
BH_FAIL("Strings aren't equal");
}
BH_StringFree(str);
}
@@ -303,6 +305,54 @@ BH_UNIT_TEST(Parity)
}
BH_UNIT_TEST(ToDouble)
{
size_t size;
BH_VERIFY(BH_ClassifyDouble(BH_StringToDouble(" INFa ", &size)) == BH_FP_INFINITE);
BH_VERIFY(size == 5);
BH_VERIFY(BH_ClassifyDouble(BH_StringToDouble(" INFINITYc ", &size)) == BH_FP_INFINITE);
BH_VERIFY(size == 10);
BH_VERIFY(BH_ClassifyDouble(BH_StringToDouble(" -INFb ", &size)) == (BH_FP_INFINITE | BH_FP_NEGATIVE));
BH_VERIFY(size == 6);
BH_VERIFY(BH_ClassifyDouble(BH_StringToDouble(" -INFINITYd ", &size)) == (BH_FP_INFINITE | BH_FP_NEGATIVE));
BH_VERIFY(size == 11);
BH_VERIFY(BH_ClassifyDouble(BH_StringToDouble(" NANe ", &size)) == BH_FP_NAN);
BH_VERIFY(size == 5);
BH_VERIFY_DELTA(BH_StringToDouble(" 1234.0312f ", &size), 1234.0312, 0.00001);
BH_VERIFY(size == 11);
BH_VERIFY_DELTA(BH_StringToDouble(" 3.14159g ", &size), 3.14159, 0.00001);
BH_VERIFY(size == 9);
BH_VERIFY(BH_StringToDouble(" h ", &size) == 0.0);
BH_VERIFY(size == 0);
BH_VERIFY(BH_StringToDouble(" 0 ", &size) == 0.0);
BH_VERIFY(size == 3);
BH_VERIFY(BH_StringToDouble(" 0.0 ", &size) == 0.0);
BH_VERIFY(size == 5);
BH_VERIFY(BH_StringToDouble(" 0. ", &size) == 0.0);
BH_VERIFY(size == 4);
BH_VERIFY(BH_StringToDouble(" .0 ", &size) == 0.0);
BH_VERIFY(size == 4);
BH_VERIFY(BH_StringToDouble(" .Hello ", &size) == 0.0);
BH_VERIFY(size == 0);
BH_VERIFY(BH_StringToDouble(" .E12 ", &size) == 0.0);
BH_VERIFY(size == 0);
return 0;
}
int main(int argc, char **argv)
{
BH_UNUSED(argc);
@@ -315,6 +365,7 @@ int main(int argc, char **argv)
BH_UNIT_ADD(ShortestFormat);
BH_UNIT_ADD(ShortestRoundTrip);
BH_UNIT_ADD(Parity);
BH_UNIT_ADD(ToDouble);
return BH_UnitRun();
}