From b943135d71189c9b12c9256d6d5cb4a85b9f570b Mon Sep 17 00:00:00 2001 From: Mikhail Romanko Date: Mon, 24 Mar 2025 22:38:11 +0300 Subject: 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. --- src/String/Core.c | 2 +- src/String/Float.c | 420 +++++++++++++++++++++++++++++++++++++++++--------- src/String/Int.c | 14 +- test/src/TestFloat.c | 371 ++++++++++++++++++++++++++++++++++++++++++++ test/src/TestString.c | 320 -------------------------------------- 5 files changed, 730 insertions(+), 397 deletions(-) create mode 100644 test/src/TestFloat.c delete mode 100644 test/src/TestString.c diff --git a/src/String/Core.c b/src/String/Core.c index 4dfef4b..85bc7d1 100644 --- a/src/String/Core.c +++ b/src/String/Core.c @@ -12,7 +12,7 @@ void BH_StringFree(char *string) char *BH_StringCopy(const char *string) { char *result; - + result = malloc(strlen(string) + 1); if (result) strcpy(result, string); diff --git a/src/String/Float.c b/src/String/Float.c index cb05017..f1e39c4 100644 --- a/src/String/Float.c +++ b/src/String/Float.c @@ -4,14 +4,17 @@ #include #include #include -#include #include +#include +/* 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); @@ -117,7 +163,7 @@ static void BIntAdd(const BInt *a, carry += a->data[i]; if (i < b->size) carry += b->data[i]; - + out->data[i] = carry & 0xFFFF; carry = (carry >> 16); } @@ -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++) @@ -145,7 +195,7 @@ static void BIntSub(const BInt *a, carry += a->data[i]; if (i < b->size) carry -= b->data[i]; - + out->data[i] = carry & 0xFFFF; carry = (carry & 0xFFFF0000) | (carry >> 16); } @@ -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; @@ -245,14 +324,14 @@ static void BIntLsh(const BInt *in, } out->data[i] = high; out->size = in->size + blocks + 1; - } + } else { for (i = in->size; i; i--) out->data[i - 1 + blocks] = in->data[i - 1]; out->size = in->size + blocks; } - + /* Trim leading zeros and zero out lower blocks */ BIntTrim(out); for (i = blocks; i; i--) @@ -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) { @@ -331,7 +422,7 @@ static void BIntDiv(const BInt *a, shift = BIntClz(b->data[b->size - 1]); BIntLsh(a, shift, &tmp[0]); BIntLsh(b, shift, &tmp[1]); - + /* Prepare first step of the division */ q->size = 0; r->size = 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) { @@ -446,7 +554,7 @@ static void dragonRound(struct DragonState *state, { for (i = *size; i && buffer[i - 1] == '9'; i--) buffer[i - 1] = '0'; - + if (i > 0) buffer[i - 1]++; else @@ -458,7 +566,7 @@ static void dragonRound(struct DragonState *state, } -static void dragon(double value, +static void dragon(double value, int precision, int mode, char *buffer, @@ -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,16 +603,16 @@ 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; - while(1) + while(1) { 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) s += state.tmp[0].data[0]; @@ -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, @@ -537,7 +650,11 @@ 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) @@ -552,9 +669,9 @@ static char *formatF(char buffer[BUFSIZE], { *(current++) = '0'; - if (precision > 0) + if (precision > 0) *(current++) = '.'; - + for (i = 0; i < MIN(-k - 1, precision); i++) *(current++) = '0'; } @@ -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) @@ -594,7 +717,7 @@ static char *formatE(char buffer[BUFSIZE], /* Add sign and digits */ if (sign) *(current++) = '-'; - + for (i = 0; i < size; i++) { *(current++) = buffer[i]; @@ -605,7 +728,7 @@ static char *formatE(char buffer[BUFSIZE], /* Pad to specified precision */ for (; i < precision + 1; i++) *(current++) = '0'; - + /* Add exponent symbol and sign */ *(current++) = "eE"[upper]; if (k < 0) @@ -717,11 +840,11 @@ char *BH_StringFromDouble(double value, static const char *infStrings[] = { "inf", "-inf", "INF", "-INF" }; static const char *nanStrings[] = { "nan", "NAN" }; int sign, type, upper; - + type = BH_ClassifyDouble(value); upper = isupper(format) > 0; sign = (type & BH_FP_NEGATIVE) != 0; - + if (sign) value = fabs(value); @@ -740,45 +863,87 @@ char *BH_StringFromDouble(double value, } -double BH_StringToDouble(const char *string, - size_t *size) +static int caselessCompare(const char *src, + const char *ref) +{ + /* Preconditions */ + assert(src != NULL && ref != NULL); + + 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) { - int nsign, esign, e, dot; const char *current; - char buffer[4]; - size_t count; + int esign, dot, count; + + /* Preconditions */ + assert(string != NULL && buffer != NULL && sign != NULL && e != NULL && type != NULL); - nsign = 0; esign = 0; e = 0; count = 0; dot = 0; + *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--; } /* Read fract part of the float */ if (*current == '.') current++; - + 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 0.0; + + 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; } diff --git a/src/String/Int.c b/src/String/Int.c index 90379d9..a9edf90 100644 --- a/src/String/Int.c +++ b/src/String/Int.c @@ -47,30 +47,30 @@ static void guessBase(const char **string, size_t *size, int *base) { - if (*base != 0) + if (*base != 0) return; *base = 10; - if (**string != '0') + if (**string != '0') return; (*string)++; - if (size) + if (size) (*size)++; switch (**string) { case 'x': case 'X': - *base = 16; + *base = 16; (*string)++; - if (size) + if (size) (*size)++; break; case 'b': case 'B': - *base = 2; + *base = 2; (*string)++; - if (size) + if (size) (*size)++; break; diff --git a/test/src/TestFloat.c b/test/src/TestFloat.c new file mode 100644 index 0000000..dac6171 --- /dev/null +++ b/test/src/TestFloat.c @@ -0,0 +1,371 @@ +#include +#include +#include +#include +#include +#include +#include +#include + + +static int compareString(double value, + int format, + int precision, + const char *ref) +{ + int result; + char *str; + + str = BH_StringFromDouble(value, format, precision); + result = strcmp(str, ref); + if (result) + printf("Value: %.17g\tReference: %s\tGot: %s\n", value, ref, str); + + BH_StringFree(str); + + return result; +} + + +static int roundtripString(double value, + int format) +{ + double result; + char *str; + + str = BH_StringFromDouble(value, format, -1); + result = BH_StringToDouble(str, NULL); + if (result != value) + printf("Value: %.17g\tGot: %.17g\tStr: %s\n", value, result, str); + + BH_StringFree(str); + + return result != value; +} + + +BH_UNIT_TEST(FixedFormat) +{ + BH_VERIFY(compareString(30.0159265358979323846, 'f', 0, "30") == 0); + BH_VERIFY(compareString(30.0159265358979323846, 'f', 1, "30.0") == 0); + BH_VERIFY(compareString(30.0159265358979323846, 'f', 2, "30.02") == 0); + BH_VERIFY(compareString(30.0159265358979323846, 'f', 3, "30.016") == 0); + BH_VERIFY(compareString(30.0159265358979323846, 'f', 4, "30.0159") == 0); + BH_VERIFY(compareString(30.0159265358979323846, 'f', 5, "30.01593") == 0); + BH_VERIFY(compareString(-30.0159265358979323846, 'f', 0, "-30") == 0); + BH_VERIFY(compareString(-30.0159265358979323846, 'f', 1, "-30.0") == 0); + BH_VERIFY(compareString(-30.0159265358979323846, 'f', 2, "-30.02") == 0); + BH_VERIFY(compareString(-30.0159265358979323846, 'f', 3, "-30.016") == 0); + BH_VERIFY(compareString(-30.0159265358979323846, 'f', 4, "-30.0159") == 0); + BH_VERIFY(compareString(-30.0159265358979323846, 'f', 5, "-30.01593") == 0); + + BH_VERIFY(compareString(0.0, 'f', 0, "0") == 0); + BH_VERIFY(compareString(0.0, 'f', 1, "0.0") == 0); + BH_VERIFY(compareString(-0.0, 'f', 0, "-0") == 0); + BH_VERIFY(compareString(-0.0, 'f', 1, "-0.0") == 0); + + BH_VERIFY(compareString(0.00015425, 'f', 0, "0") == 0); + BH_VERIFY(compareString(0.00015425, 'f', 1, "0.0") == 0); + BH_VERIFY(compareString(0.00015425, 'f', 2, "0.00") == 0); + BH_VERIFY(compareString(0.00015425, 'f', 3, "0.000") == 0); + BH_VERIFY(compareString(0.00015425, 'f', 4, "0.0002") == 0); + BH_VERIFY(compareString(0.00015425, 'f', 5, "0.00015") == 0); + BH_VERIFY(compareString(-0.00015425, 'f', 0, "-0") == 0); + BH_VERIFY(compareString(-0.00015425, 'f', 1, "-0.0") == 0); + BH_VERIFY(compareString(-0.00015425, 'f', 2, "-0.00") == 0); + BH_VERIFY(compareString(-0.00015425, 'f', 3, "-0.000") == 0); + BH_VERIFY(compareString(-0.00015425, 'f', 4, "-0.0002") == 0); + BH_VERIFY(compareString(-0.00015425, 'f', 5, "-0.00015") == 0); + + BH_VERIFY(compareString(1234567.1234, 'f', 0, "1234567") == 0); + BH_VERIFY(compareString(-1234567.1234, 'f', 0, "-1234567") == 0); + BH_VERIFY(compareString(1230000000.00123, 'f', 0, "1230000000") == 0); + BH_VERIFY(compareString(1230000000.00123, 'f', 1, "1230000000.0") == 0); + BH_VERIFY(compareString(1230000000.00123, 'f', 2, "1230000000.00") == 0); + BH_VERIFY(compareString(1230000000.00123, 'f', 3, "1230000000.001") == 0); + BH_VERIFY(compareString(-1230000000.00123, 'f', 0, "-1230000000") == 0); + BH_VERIFY(compareString(-1230000000.00123, 'f', 1, "-1230000000.0") == 0); + BH_VERIFY(compareString(-1230000000.00123, 'f', 2, "-1230000000.00") == 0); + BH_VERIFY(compareString(-1230000000.00123, 'f', 3, "-1230000000.001") == 0); + + return 0; +} + + +BH_UNIT_TEST(FixedRoundTrip) +{ + BH_VERIFY(roundtripString(1.0, 'f') == 0); + BH_VERIFY(roundtripString(-1.0, 'f') == 0); + BH_VERIFY(roundtripString(0.0, 'f') == 0); + BH_VERIFY(roundtripString(-0.0, 'f') == 0); + BH_VERIFY(roundtripString(3.14159, 'f') == 0); + BH_VERIFY(roundtripString(-3.14159, 'f') == 0); + BH_VERIFY(roundtripString(123000000.0, 'f') == 0); + BH_VERIFY(roundtripString(-123000000.0, 'f') == 0); + BH_VERIFY(roundtripString(0.81, 'f') == 0); + 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.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; +} + + +BH_UNIT_TEST(ScientificFormat) +{ + BH_VERIFY(compareString(30.0159265358979323846, 'e', 0, "3e+01") == 0); + BH_VERIFY(compareString(30.0159265358979323846, 'e', 1, "3.0e+01") == 0); + BH_VERIFY(compareString(30.0159265358979323846, 'e', 2, "3.00e+01") == 0); + BH_VERIFY(compareString(30.0159265358979323846, 'e', 3, "3.002e+01") == 0); + BH_VERIFY(compareString(30.0159265358979323846, 'e', 4, "3.0016e+01") == 0); + BH_VERIFY(compareString(30.0159265358979323846, 'e', 5, "3.00159e+01") == 0); + BH_VERIFY(compareString(-30.0159265358979323846, 'e', 0, "-3e+01") == 0); + BH_VERIFY(compareString(-30.0159265358979323846, 'e', 1, "-3.0e+01") == 0); + BH_VERIFY(compareString(-30.0159265358979323846, 'e', 2, "-3.00e+01") == 0); + BH_VERIFY(compareString(-30.0159265358979323846, 'e', 3, "-3.002e+01") == 0); + BH_VERIFY(compareString(-30.0159265358979323846, 'e', 4, "-3.0016e+01") == 0); + BH_VERIFY(compareString(-30.0159265358979323846, 'e', 5, "-3.00159e+01") == 0); + + BH_VERIFY(compareString(0.0, 'e', 0, "0e+00") == 0); + BH_VERIFY(compareString(0.0, 'e', 1, "0.0e+00") == 0); + BH_VERIFY(compareString(-0.0, 'e', 0, "-0e+00") == 0); + BH_VERIFY(compareString(-0.0, 'e', 1, "-0.0e+00") == 0); + + BH_VERIFY(compareString(0.00015425, 'e', 0, "2e-04") == 0); + BH_VERIFY(compareString(0.00015425, 'e', 1, "1.5e-04") == 0); + BH_VERIFY(compareString(0.00015425, 'e', 2, "1.54e-04") == 0); + BH_VERIFY(compareString(0.00015425, 'e', 3, "1.543e-04") == 0); + BH_VERIFY(compareString(0.00015425, 'e', 4, "1.5425e-04") == 0); + BH_VERIFY(compareString(0.00015425, 'e', 5, "1.54250e-04") == 0); + BH_VERIFY(compareString(-0.00015425, 'e', 0, "-2e-04") == 0); + BH_VERIFY(compareString(-0.00015425, 'e', 1, "-1.5e-04") == 0); + BH_VERIFY(compareString(-0.00015425, 'e', 2, "-1.54e-04") == 0); + BH_VERIFY(compareString(-0.00015425, 'e', 3, "-1.543e-04") == 0); + BH_VERIFY(compareString(-0.00015425, 'e', 4, "-1.5425e-04") == 0); + BH_VERIFY(compareString(-0.00015425, 'e', 5, "-1.54250e-04") == 0); + + BH_VERIFY(compareString(1234567.1234, 'e', 0, "1e+06") == 0); + BH_VERIFY(compareString(-1234567.1234, 'e', 0, "-1e+06") == 0); + BH_VERIFY(compareString(1230000000.00123, 'e', 0, "1e+09") == 0); + BH_VERIFY(compareString(1230000000.00123, 'e', 1, "1.2e+09") == 0); + BH_VERIFY(compareString(1230000000.00123, 'e', 2, "1.23e+09") == 0); + BH_VERIFY(compareString(1230000000.00123, 'e', 3, "1.230e+09") == 0); + BH_VERIFY(compareString(-1230000000.00123, 'e', 0, "-1e+09") == 0); + BH_VERIFY(compareString(-1230000000.00123, 'e', 1, "-1.2e+09") == 0); + BH_VERIFY(compareString(-1230000000.00123, 'e', 2, "-1.23e+09") == 0); + BH_VERIFY(compareString(-1230000000.00123, 'e', 3, "-1.230e+09") == 0); + + return 0; +} + + +BH_UNIT_TEST(ShortestFormat) +{ + BH_VERIFY(compareString(30.0159265358979323846, 'g', 0, "3e+01") == 0); + BH_VERIFY(compareString(30.0159265358979323846, 'g', 1, "3e+01") == 0); + BH_VERIFY(compareString(30.0159265358979323846, 'g', 2, "30") == 0); + BH_VERIFY(compareString(30.0159265358979323846, 'g', 3, "30") == 0); + BH_VERIFY(compareString(30.0159265358979323846, 'g', 4, "30.02") == 0); + BH_VERIFY(compareString(30.0159265358979323846, 'g', 5, "30.016") == 0); + BH_VERIFY(compareString(-30.0159265358979323846, 'g', 0, "-3e+01") == 0); + BH_VERIFY(compareString(-30.0159265358979323846, 'g', 1, "-3e+01") == 0); + BH_VERIFY(compareString(-30.0159265358979323846, 'g', 2, "-30") == 0); + BH_VERIFY(compareString(-30.0159265358979323846, 'g', 3, "-30") == 0); + BH_VERIFY(compareString(-30.0159265358979323846, 'g', 4, "-30.02") == 0); + BH_VERIFY(compareString(-30.0159265358979323846, 'g', 5, "-30.016") == 0); + + BH_VERIFY(compareString(0.0, 'g', 0, "0") == 0); + BH_VERIFY(compareString(0.0, 'g', 1, "0") == 0); + BH_VERIFY(compareString(-0.0, 'g', 0, "-0") == 0); + BH_VERIFY(compareString(-0.0, 'g', 1, "-0") == 0); + + BH_VERIFY(compareString(0.00015425, 'g', 0, "0.0002") == 0); + BH_VERIFY(compareString(0.00015425, 'g', 1, "0.0002") == 0); + BH_VERIFY(compareString(0.00015425, 'g', 2, "0.00015") == 0); + BH_VERIFY(compareString(0.00015425, 'g', 3, "0.000154") == 0); + BH_VERIFY(compareString(0.00015425, 'g', 4, "0.0001543") == 0); + BH_VERIFY(compareString(0.00015425, 'g', 5, "0.00015425") == 0); + BH_VERIFY(compareString(-0.00015425, 'g', 0, "-0.0002") == 0); + BH_VERIFY(compareString(-0.00015425, 'g', 1, "-0.0002") == 0); + BH_VERIFY(compareString(-0.00015425, 'g', 2, "-0.00015") == 0); + BH_VERIFY(compareString(-0.00015425, 'g', 3, "-0.000154") == 0); + BH_VERIFY(compareString(-0.00015425, 'g', 4, "-0.0001543") == 0); + BH_VERIFY(compareString(-0.00015425, 'g', 5, "-0.00015425") == 0); + + BH_VERIFY(compareString(1234567.1234, 'g', 0, "1e+06") == 0); + BH_VERIFY(compareString(-1234567.1234, 'g', 0, "-1e+06") == 0); + BH_VERIFY(compareString(1230000000.00123, 'g', 0, "1e+09") == 0); + 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(-1230000000.00123, 'g', 0, "-1e+09") == 0); + 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.0, 'g', 17, "1.4411518807585587e+17") == 0); + BH_VERIFY(compareString(-144115188075855877.0, 'g', 17, "-1.4411518807585587e+17") == 0); + + return 0; +} + + +BH_UNIT_TEST(ScientificRoundTrip) +{ + BH_VERIFY(roundtripString(1.0, 'e') == 0); + BH_VERIFY(roundtripString(-1.0, 'e') == 0); + BH_VERIFY(roundtripString(0.0, 'e') == 0); + BH_VERIFY(roundtripString(-0.0, 'e') == 0); + BH_VERIFY(roundtripString(3.14159, 'e') == 0); + BH_VERIFY(roundtripString(-3.14159, 'e') == 0); + BH_VERIFY(roundtripString(123000000.0, 'e') == 0); + BH_VERIFY(roundtripString(-123000000.0, 'e') == 0); + BH_VERIFY(roundtripString(0.81, 'e') == 0); + 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.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; +} + + +BH_UNIT_TEST(ShortestRoundTrip) +{ + BH_VERIFY(roundtripString(1.0, 'g') == 0); + BH_VERIFY(roundtripString(-1.0, 'g') == 0); + BH_VERIFY(roundtripString(0.0, 'g') == 0); + BH_VERIFY(roundtripString(-0.0, 'g') == 0); + BH_VERIFY(roundtripString(3.14159, 'g') == 0); + BH_VERIFY(roundtripString(-3.14159, 'g') == 0); + BH_VERIFY(roundtripString(123000000.0, 'g') == 0); + BH_VERIFY(roundtripString(-123000000.0, 'g') == 0); + BH_VERIFY(roundtripString(0.81, 'g') == 0); + 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.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; +} + + +BH_UNIT_TEST(Parity) +{ + char buffer[16], output[2000]; + uint64_t frac; + double value; + int i, j, k; + + for (i = 0; i < 100; i++) + { + frac = (rand() & 0x7FFF); + frac = (frac << 15) | (rand() & 0x7FFF); + frac = (frac << 15) | (rand() & 0x7FFF); + frac = (frac << 15) | (rand() & 0x7FFF); + + for (j = 0; j < 100; j++) + { + value = frac * pow(2, (rand() % 2046) - 1024); + + for (k = 0; k < 18; k++) + { + char *str; + + sprintf(buffer, "%%.%dg", k); + str = BH_StringFromDouble(value, 'g', k); + sprintf(output, buffer, value); + + if (strcmp(str, output)) + { + printf("(%.17g) (%d) %s vs %s\n", value, k, str, output); + BH_FAIL("Strings aren't equal"); + } + BH_StringFree(str); + } + } + } + + return 0; +} + + +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); + BH_UNUSED(argv); + + BH_UNIT_ADD(FixedFormat); + BH_UNIT_ADD(FixedRoundTrip); + BH_UNIT_ADD(ScientificFormat); + BH_UNIT_ADD(ScientificRoundTrip); + BH_UNIT_ADD(ShortestFormat); + BH_UNIT_ADD(ShortestRoundTrip); + BH_UNIT_ADD(Parity); + BH_UNIT_ADD(ToDouble); + + return BH_UnitRun(); +} diff --git a/test/src/TestString.c b/test/src/TestString.c deleted file mode 100644 index 53811cf..0000000 --- a/test/src/TestString.c +++ /dev/null @@ -1,320 +0,0 @@ -#include -#include -#include -#include -#include -#include - - -static int compareString(double value, - int format, - int precision, - const char *ref) -{ - int result; - char *str; - - str = BH_StringFromDouble(value, format, precision); - result = strcmp(str, ref); - if (result) - printf("Value: %.17g\tReference: %s\tGot: %s\n", value, ref, str); - - BH_StringFree(str); - - return result; -} - - -static int roundtripString(double value, - int format) -{ - double result; - char *str; - - str = BH_StringFromDouble(value, format, -1); - result = strtod(str, NULL); - if (result != value) - printf("Value: %.17g\tGot: %.17g\tStr: %s\n", value, result, str); - - BH_StringFree(str); - - return result != value; -} - - -BH_UNIT_TEST(FixedFormat) -{ - BH_VERIFY(compareString(30.0159265358979323846, 'f', 0, "30") == 0); - BH_VERIFY(compareString(30.0159265358979323846, 'f', 1, "30.0") == 0); - BH_VERIFY(compareString(30.0159265358979323846, 'f', 2, "30.02") == 0); - BH_VERIFY(compareString(30.0159265358979323846, 'f', 3, "30.016") == 0); - BH_VERIFY(compareString(30.0159265358979323846, 'f', 4, "30.0159") == 0); - BH_VERIFY(compareString(30.0159265358979323846, 'f', 5, "30.01593") == 0); - BH_VERIFY(compareString(-30.0159265358979323846, 'f', 0, "-30") == 0); - BH_VERIFY(compareString(-30.0159265358979323846, 'f', 1, "-30.0") == 0); - BH_VERIFY(compareString(-30.0159265358979323846, 'f', 2, "-30.02") == 0); - BH_VERIFY(compareString(-30.0159265358979323846, 'f', 3, "-30.016") == 0); - BH_VERIFY(compareString(-30.0159265358979323846, 'f', 4, "-30.0159") == 0); - BH_VERIFY(compareString(-30.0159265358979323846, 'f', 5, "-30.01593") == 0); - - BH_VERIFY(compareString(0.0, 'f', 0, "0") == 0); - BH_VERIFY(compareString(0.0, 'f', 1, "0.0") == 0); - BH_VERIFY(compareString(-0.0, 'f', 0, "-0") == 0); - BH_VERIFY(compareString(-0.0, 'f', 1, "-0.0") == 0); - - BH_VERIFY(compareString(0.00015425, 'f', 0, "0") == 0); - BH_VERIFY(compareString(0.00015425, 'f', 1, "0.0") == 0); - BH_VERIFY(compareString(0.00015425, 'f', 2, "0.00") == 0); - BH_VERIFY(compareString(0.00015425, 'f', 3, "0.000") == 0); - BH_VERIFY(compareString(0.00015425, 'f', 4, "0.0002") == 0); - BH_VERIFY(compareString(0.00015425, 'f', 5, "0.00015") == 0); - BH_VERIFY(compareString(-0.00015425, 'f', 0, "-0") == 0); - BH_VERIFY(compareString(-0.00015425, 'f', 1, "-0.0") == 0); - BH_VERIFY(compareString(-0.00015425, 'f', 2, "-0.00") == 0); - BH_VERIFY(compareString(-0.00015425, 'f', 3, "-0.000") == 0); - BH_VERIFY(compareString(-0.00015425, 'f', 4, "-0.0002") == 0); - BH_VERIFY(compareString(-0.00015425, 'f', 5, "-0.00015") == 0); - - BH_VERIFY(compareString(1234567.1234, 'f', 0, "1234567") == 0); - BH_VERIFY(compareString(-1234567.1234, 'f', 0, "-1234567") == 0); - BH_VERIFY(compareString(1230000000.00123, 'f', 0, "1230000000") == 0); - BH_VERIFY(compareString(1230000000.00123, 'f', 1, "1230000000.0") == 0); - BH_VERIFY(compareString(1230000000.00123, 'f', 2, "1230000000.00") == 0); - BH_VERIFY(compareString(1230000000.00123, 'f', 3, "1230000000.001") == 0); - BH_VERIFY(compareString(-1230000000.00123, 'f', 0, "-1230000000") == 0); - BH_VERIFY(compareString(-1230000000.00123, 'f', 1, "-1230000000.0") == 0); - BH_VERIFY(compareString(-1230000000.00123, 'f', 2, "-1230000000.00") == 0); - BH_VERIFY(compareString(-1230000000.00123, 'f', 3, "-1230000000.001") == 0); - - return 0; -} - - -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); - BH_VERIFY(roundtripString(-3.14159, 'f') == 0); - BH_VERIFY(roundtripString(123000000.0, 'f') == 0); - BH_VERIFY(roundtripString(-123000000.0, 'f') == 0); - BH_VERIFY(roundtripString(0.81, 'f') == 0); - 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); - - return 0; -} - - -BH_UNIT_TEST(ScientificFormat) -{ - BH_VERIFY(compareString(30.0159265358979323846, 'e', 0, "3e+01") == 0); - BH_VERIFY(compareString(30.0159265358979323846, 'e', 1, "3.0e+01") == 0); - BH_VERIFY(compareString(30.0159265358979323846, 'e', 2, "3.00e+01") == 0); - BH_VERIFY(compareString(30.0159265358979323846, 'e', 3, "3.002e+01") == 0); - BH_VERIFY(compareString(30.0159265358979323846, 'e', 4, "3.0016e+01") == 0); - BH_VERIFY(compareString(30.0159265358979323846, 'e', 5, "3.00159e+01") == 0); - BH_VERIFY(compareString(-30.0159265358979323846, 'e', 0, "-3e+01") == 0); - BH_VERIFY(compareString(-30.0159265358979323846, 'e', 1, "-3.0e+01") == 0); - BH_VERIFY(compareString(-30.0159265358979323846, 'e', 2, "-3.00e+01") == 0); - BH_VERIFY(compareString(-30.0159265358979323846, 'e', 3, "-3.002e+01") == 0); - BH_VERIFY(compareString(-30.0159265358979323846, 'e', 4, "-3.0016e+01") == 0); - BH_VERIFY(compareString(-30.0159265358979323846, 'e', 5, "-3.00159e+01") == 0); - - BH_VERIFY(compareString(0.0, 'e', 0, "0e+00") == 0); - BH_VERIFY(compareString(0.0, 'e', 1, "0.0e+00") == 0); - BH_VERIFY(compareString(-0.0, 'e', 0, "-0e+00") == 0); - BH_VERIFY(compareString(-0.0, 'e', 1, "-0.0e+00") == 0); - - BH_VERIFY(compareString(0.00015425, 'e', 0, "2e-04") == 0); - BH_VERIFY(compareString(0.00015425, 'e', 1, "1.5e-04") == 0); - BH_VERIFY(compareString(0.00015425, 'e', 2, "1.54e-04") == 0); - BH_VERIFY(compareString(0.00015425, 'e', 3, "1.543e-04") == 0); - BH_VERIFY(compareString(0.00015425, 'e', 4, "1.5425e-04") == 0); - BH_VERIFY(compareString(0.00015425, 'e', 5, "1.54250e-04") == 0); - BH_VERIFY(compareString(-0.00015425, 'e', 0, "-2e-04") == 0); - BH_VERIFY(compareString(-0.00015425, 'e', 1, "-1.5e-04") == 0); - BH_VERIFY(compareString(-0.00015425, 'e', 2, "-1.54e-04") == 0); - BH_VERIFY(compareString(-0.00015425, 'e', 3, "-1.543e-04") == 0); - BH_VERIFY(compareString(-0.00015425, 'e', 4, "-1.5425e-04") == 0); - BH_VERIFY(compareString(-0.00015425, 'e', 5, "-1.54250e-04") == 0); - - BH_VERIFY(compareString(1234567.1234, 'e', 0, "1e+06") == 0); - BH_VERIFY(compareString(-1234567.1234, 'e', 0, "-1e+06") == 0); - BH_VERIFY(compareString(1230000000.00123, 'e', 0, "1e+09") == 0); - BH_VERIFY(compareString(1230000000.00123, 'e', 1, "1.2e+09") == 0); - BH_VERIFY(compareString(1230000000.00123, 'e', 2, "1.23e+09") == 0); - BH_VERIFY(compareString(1230000000.00123, 'e', 3, "1.230e+09") == 0); - BH_VERIFY(compareString(-1230000000.00123, 'e', 0, "-1e+09") == 0); - BH_VERIFY(compareString(-1230000000.00123, 'e', 1, "-1.2e+09") == 0); - BH_VERIFY(compareString(-1230000000.00123, 'e', 2, "-1.23e+09") == 0); - BH_VERIFY(compareString(-1230000000.00123, 'e', 3, "-1.230e+09") == 0); - - return 0; -} - - -BH_UNIT_TEST(ShortestFormat) -{ - BH_VERIFY(compareString(30.0159265358979323846, 'g', 0, "3e+01") == 0); - BH_VERIFY(compareString(30.0159265358979323846, 'g', 1, "3e+01") == 0); - BH_VERIFY(compareString(30.0159265358979323846, 'g', 2, "30") == 0); - BH_VERIFY(compareString(30.0159265358979323846, 'g', 3, "30") == 0); - BH_VERIFY(compareString(30.0159265358979323846, 'g', 4, "30.02") == 0); - BH_VERIFY(compareString(30.0159265358979323846, 'g', 5, "30.016") == 0); - BH_VERIFY(compareString(-30.0159265358979323846, 'g', 0, "-3e+01") == 0); - BH_VERIFY(compareString(-30.0159265358979323846, 'g', 1, "-3e+01") == 0); - BH_VERIFY(compareString(-30.0159265358979323846, 'g', 2, "-30") == 0); - BH_VERIFY(compareString(-30.0159265358979323846, 'g', 3, "-30") == 0); - BH_VERIFY(compareString(-30.0159265358979323846, 'g', 4, "-30.02") == 0); - BH_VERIFY(compareString(-30.0159265358979323846, 'g', 5, "-30.016") == 0); - - BH_VERIFY(compareString(0.0, 'g', 0, "0") == 0); - BH_VERIFY(compareString(0.0, 'g', 1, "0") == 0); - BH_VERIFY(compareString(-0.0, 'g', 0, "-0") == 0); - BH_VERIFY(compareString(-0.0, 'g', 1, "-0") == 0); - - BH_VERIFY(compareString(0.00015425, 'g', 0, "0.0002") == 0); - BH_VERIFY(compareString(0.00015425, 'g', 1, "0.0002") == 0); - BH_VERIFY(compareString(0.00015425, 'g', 2, "0.00015") == 0); - BH_VERIFY(compareString(0.00015425, 'g', 3, "0.000154") == 0); - BH_VERIFY(compareString(0.00015425, 'g', 4, "0.0001543") == 0); - BH_VERIFY(compareString(0.00015425, 'g', 5, "0.00015425") == 0); - BH_VERIFY(compareString(-0.00015425, 'g', 0, "-0.0002") == 0); - BH_VERIFY(compareString(-0.00015425, 'g', 1, "-0.0002") == 0); - BH_VERIFY(compareString(-0.00015425, 'g', 2, "-0.00015") == 0); - BH_VERIFY(compareString(-0.00015425, 'g', 3, "-0.000154") == 0); - BH_VERIFY(compareString(-0.00015425, 'g', 4, "-0.0001543") == 0); - BH_VERIFY(compareString(-0.00015425, 'g', 5, "-0.00015425") == 0); - - BH_VERIFY(compareString(1234567.1234, 'g', 0, "1e+06") == 0); - BH_VERIFY(compareString(-1234567.1234, 'g', 0, "-1e+06") == 0); - BH_VERIFY(compareString(1230000000.00123, 'g', 0, "1e+09") == 0); - 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(-1230000000.00123, 'g', 0, "-1e+09") == 0); - 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); - - return 0; -} - - -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); - BH_VERIFY(roundtripString(-3.14159, 'e') == 0); - BH_VERIFY(roundtripString(123000000.0, 'e') == 0); - BH_VERIFY(roundtripString(-123000000.0, 'e') == 0); - BH_VERIFY(roundtripString(0.81, 'e') == 0); - 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); - - return 0; -} - - -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); - BH_VERIFY(roundtripString(-3.14159, 'g') == 0); - BH_VERIFY(roundtripString(123000000.0, 'g') == 0); - BH_VERIFY(roundtripString(-123000000.0, 'g') == 0); - BH_VERIFY(roundtripString(0.81, 'g') == 0); - 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); - - return 0; -} - - -BH_UNIT_TEST(Parity) -{ - char buffer[16], output[2000]; - uint64_t frac; - double value; - int i, j, k; - - for (i = 0; i < 100; i++) - { - frac = (rand() & 0x7FFF); - frac = (frac << 15) | (rand() & 0x7FFF); - frac = (frac << 15) | (rand() & 0x7FFF); - frac = (frac << 15) | (rand() & 0x7FFF); - - for (j = 0; j < 100; j++) - { - value = frac * pow(2, (rand() % 2046) - 1024); - - for (k = 0; k < 18; k++) - { - char *str; - - sprintf(buffer, "%%.%dg", k); - str = BH_StringFromDouble(value, 'g', k); - sprintf(output, buffer, value); - - if (strcmp(str, output)) - { - printf("(%.17g) (%d) %s vs %s\n", value, k, str, output); - BH_FAIL("Not equal"); - } - BH_StringFree(str); - } - } - } - - return 0; -} - - -int main(int argc, char **argv) -{ - BH_UNUSED(argc); - BH_UNUSED(argv); - - BH_UNIT_ADD(FixedFormat); - BH_UNIT_ADD(FixedRoundTrip); - BH_UNIT_ADD(ScientificFormat); - BH_UNIT_ADD(ScientificRoundTrip); - BH_UNIT_ADD(ShortestFormat); - BH_UNIT_ADD(ShortestRoundTrip); - BH_UNIT_ADD(Parity); - - return BH_UnitRun(); -} -- cgit v1.2.3