Added fromString.

This commit is contained in:
Branimir Karadžić
2017-06-17 14:50:29 -07:00
parent bf29ace5ee
commit 65eb5d12d4
3 changed files with 577 additions and 8 deletions

View File

@@ -239,6 +239,9 @@ namespace bx
///
int32_t toString(char* _out, int32_t _max, uint64_t _value, uint32_t _base = 10);
///
double fromString(const char* _str);
} // namespace bx
#include "inline/string.inl"

View File

@@ -105,15 +105,15 @@ namespace bx
void NormalizedBoundaries(DiyFp* minus, DiyFp* plus) const
{
DiyFp pl = DiyFp((f << 1) + 1, e - 1).NormalizeBoundary();
DiyFp mi = (f == kDpHiddenBit) ? DiyFp((f << 2) - 1, e - 2) : DiyFp((f << 1) - 1, e - 1);
DiyFp pl = DiyFp( (f << 1) + 1, e - 1).NormalizeBoundary();
DiyFp mi = (f == kDpHiddenBit) ? DiyFp( (f << 2) - 1, e - 2) : DiyFp( (f << 1) - 1, e - 1);
mi.f <<= mi.e - pl.e;
mi.e = pl.e;
*plus = pl;
*minus = mi;
}
#define UINT64_C2(h, l) ((static_cast<uint64_t>(h) << 32) | static_cast<uint64_t>(l))
#define UINT64_C2(h, l) ( (static_cast<uint64_t>(h) << 32) | static_cast<uint64_t>(l) )
static const int32_t kDiySignificandSize = 64;
static const int32_t kDpSignificandSize = 52;
@@ -217,10 +217,10 @@ namespace bx
k++;
}
uint32_t index = static_cast<uint32_t>((k >> 3) + 1);
*K = -(-348 + static_cast<int32_t>(index << 3)); // decimal exponent no need lookup table
uint32_t index = static_cast<uint32_t>( (k >> 3) + 1);
*K = -(-348 + static_cast<int32_t>(index << 3) ); // decimal exponent no need lookup table
BX_CHECK(index < sizeof(s_kCachedPowers_F) / sizeof(s_kCachedPowers_F[0]));
BX_CHECK(index < sizeof(s_kCachedPowers_F) / sizeof(s_kCachedPowers_F[0]) );
return DiyFp(s_kCachedPowers_F[index], s_kCachedPowers_E[index]);
}
@@ -228,7 +228,7 @@ namespace bx
{
while (rest < wp_w
&& delta - rest >= ten_kappa
&& (rest + ten_kappa < wp_w || wp_w - rest > rest + ten_kappa - wp_w))
&& (rest + ten_kappa < wp_w || wp_w - rest > rest + ten_kappa - wp_w) )
{
buffer[len - 1]--;
rest += ten_kappa;
@@ -256,7 +256,7 @@ namespace bx
const DiyFp wp_w = Mp - W;
uint32_t p1 = static_cast<uint32_t>(Mp.f >> -one.e);
uint64_t p2 = Mp.f & (one.f - 1);
int32_t kappa = static_cast<int32_t>(CountDecimalDigit32(p1));
int32_t kappa = static_cast<int32_t>(CountDecimalDigit32(p1) );
*len = 0;
while (kappa > 0)
@@ -557,4 +557,527 @@ namespace bx
return toStringUnsigned(_dst, _max, _value, _base);
}
/*
* MIT License
*
* Copyright (c) 2016 Grzegorz Kraszewski
*
* 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.
*/
/*
* IMPORTANT
*
* The code works in "round towards zero" mode. This is different from
* GCC standard library strtod(), which uses "round half to even" rule.
* Therefore it cannot be used as a direct drop-in replacement, as in
* some cases results will be different on the least significant bit of
* mantissa. Read more in the README.md file.
*/
#define DIGITS 18
#define DOUBLE_PLUS_ZERO 0x0000000000000000ull
#define DOUBLE_MINUS_ZERO 0x8000000000000000ull
#define DOUBLE_PLUS_INFINITY 0x7ff0000000000000ull
#define DOUBLE_MINUS_INFINITY 0xfff0000000000000ull
union HexDouble
{
double d;
uint64_t u;
};
#define lsr96(s2, s1, s0, d2, d1, d0) \
d0 = ( (s0) >> 1) | ( ( (s1) & 1) << 31); \
d1 = ( (s1) >> 1) | ( ( (s2) & 1) << 31); \
d2 = (s2) >> 1;
#define lsl96(s2, s1, s0, d2, d1, d0) \
d2 = ( (s2) << 1) | ( ( (s1) & (1 << 31) ) >> 31); \
d1 = ( (s1) << 1) | ( ( (s0) & (1 << 31) ) >> 31); \
d0 = (s0) << 1;
/*
* Undefine the below constant if your processor or compiler is slow
* at 64-bit arithmetic. This is a rare case however. 64-bit macros are
* better for deeply pipelined CPUs (no conditional execution), are
* very efficient for 64-bit processors and also fast on 32-bit processors
* featuring extended precision arithmetic (x86, PowerPC_32, M68k and probably
* more).
*/
#define USE_64BIT_FOR_ADDSUB_MACROS 1
#if USE_64BIT_FOR_ADDSUB_MACROS
#define add96(s2, s1, s0, d2, d1, d0) { \
uint64_t w; \
w = (uint64_t)(s0) + (uint64_t)(d0); \
(s0) = w; \
w >>= 32; \
w += (uint64_t)(s1) + (uint64_t)(d1); \
(s1) = w; \
w >>= 32; \
w += (uint64_t)(s2) + (uint64_t)(d2); \
(s2) = w; }
#define sub96(s2, s1, s0, d2, d1, d0) { \
uint64_t w; \
w = (uint64_t)(s0) - (uint64_t)(d0); \
(s0) = w; \
w >>= 32; \
w += (uint64_t)(s1) - (uint64_t)(d1); \
(s1) = w; \
w >>= 32; \
w += (uint64_t)(s2) - (uint64_t)(d2); \
(s2) = w; }
#else
#define add96(s2, s1, s0, d2, d1, d0) { \
uint32_t _x, _c; \
_x = (s0); (s0) += (d0); \
if ( (s0) < _x) _c = 1; else _c = 0; \
_x = (s1); (s1) += (d1) + _c; \
if ( ( (s1) < _x) || ( ( (s1) == _x) && _c) ) _c = 1; else _c = 0; \
(s2) += (d2) + _c; }
#define sub96(s2, s1, s0, d2, d1, d0) { \
uint32_t _x, _c; \
_x = (s0); (s0) -= (d0); \
if ( (s0) > _x) _c = 1; else _c = 0; \
_x = (s1); (s1) -= (d1) + _c; \
if ( ( (s1) > _x) || ( ( (s1) == _x) && _c) ) _c = 1; else _c = 0; \
(s2) -= (d2) + _c; }
#endif /* USE_64BIT_FOR_ADDSUB_MACROS */
/* parser state machine states */
#define FSM_A 0
#define FSM_B 1
#define FSM_C 2
#define FSM_D 3
#define FSM_E 4
#define FSM_F 5
#define FSM_G 6
#define FSM_H 7
#define FSM_I 8
#define FSM_STOP 9
/* Modify these if working with non-ASCII encoding */
#define DPOINT '.'
#define ISEXP(x) ( ( (x) == 'E') || ( (x) == 'e') )
/* The structure is filled by parser, then given to converter. */
struct PrepNumber
{
int negative; /* 0 if positive number, 1 if negative */
int32_t exponent; /* power of 10 exponent */
uint64_t mantissa; /* integer mantissa */
};
/* Possible parser return values. */
#define PARSER_OK 0 // parser finished OK
#define PARSER_PZERO 1 // no digits or number is smaller than +-2^-1022
#define PARSER_MZERO 2 // number is negative, module smaller
#define PARSER_PINF 3 // number is higher than +HUGE_VAL
#define PARSER_MINF 4 // number is lower than -HUGE_VAL
/* GETC() macro gets next character from processed string. */
#define GETC(s) *s++
static int parser(const char *s, struct PrepNumber *pn)
{
int state = FSM_A;
int digx = 0, c = ' '; /* initial value for kicking off the state machine */
int result = PARSER_OK;
int expneg = 0;
int32_t expexp = 0;
while (state != FSM_STOP)
{
switch (state)
{
case FSM_A:
if (isSpace(c) )
{
c = GETC(s);
}
else
{
state = FSM_B;
}
break;
case FSM_B:
state = FSM_C;
if (c == '+')
{
c = GETC(s);
}
else if (c == '-')
{
pn->negative = 1;
c = GETC(s);
}
else if (isNumeric(c) )
{
}
else if (c == DPOINT)
{
}
else
{
state = FSM_STOP;
}
break;
case FSM_C:
if (c == '0')
{
c = GETC(s);
}
else if (c == DPOINT)
{
c = GETC(s);
state = FSM_D;
}
else
{
state = FSM_E;
}
break;
case FSM_D:
if (c == '0')
{
c = GETC(s);
if (pn->exponent > -2147483647) pn->exponent--;
}
else
{
state = FSM_F;
}
break;
case FSM_E:
if (isNumeric(c) )
{
if (digx < DIGITS)
{
pn->mantissa *= 10;
pn->mantissa += c - '0';
digx++;
}
else if (pn->exponent < 2147483647)
{
pn->exponent++;
}
c = GETC(s);
}
else if (c == DPOINT)
{
c = GETC(s);
state = FSM_F;
}
else
{
state = FSM_F;
}
break;
case FSM_F:
if (isNumeric(c) )
{
if (digx < DIGITS)
{
pn->mantissa *= 10;
pn->mantissa += c - '0';
pn->exponent--;
digx++;
}
c = GETC(s);
}
else if (ISEXP(c) )
{
c = GETC(s);
state = FSM_G;
}
else
{
state = FSM_G;
}
break;
case FSM_G:
if (c == '+')
{
c = GETC(s);
}
else if (c == '-')
{
expneg = 1;
c = GETC(s);
}
state = FSM_H;
break;
case FSM_H:
if (c == '0')
{
c = GETC(s);
}
else
{
state = FSM_I;
}
break;
case FSM_I:
if (isNumeric(c) )
{
if (expexp < 214748364)
{
expexp *= 10;
expexp += c - '0';
}
c = GETC(s);
}
else
{
state = FSM_STOP;
}
break;
}
}
if (expneg)
{
expexp = -expexp;
}
pn->exponent += expexp;
if (pn->mantissa == 0)
{
if (pn->negative)
{
result = PARSER_MZERO;
}
else
{
result = PARSER_PZERO;
}
}
else if (pn->exponent > 309)
{
if (pn->negative)
{
result = PARSER_MINF;
}
else
{
result = PARSER_PINF;
}
}
else if (pn->exponent < -328)
{
if (pn->negative)
{
result = PARSER_MZERO;
}
else
{
result = PARSER_PZERO;
}
}
return result;
}
static double converter(struct PrepNumber *pn)
{
int binexp = 92;
union HexDouble hd;
uint32_t s2, s1, s0; /* 96-bit precision integer */
uint32_t q2, q1, q0; /* 96-bit precision integer */
uint32_t r2, r1, r0; /* 96-bit precision integer */
uint32_t mask28 = 0xF << 28;
hd.u = 0;
s0 = (uint32_t)(pn->mantissa & 0xFFFFFFFF);
s1 = (uint32_t)(pn->mantissa >> 32);
s2 = 0;
while (pn->exponent > 0)
{
lsl96(s2, s1, s0, q2, q1, q0); // q = p << 1
lsl96(q2, q1, q0, r2, r1, r0); // r = p << 2
lsl96(r2, r1, r0, s2, s1, s0); // p = p << 3
add96(s2, s1, s0, q2, q1, q0); // p = (p << 3) + (p << 1)
pn->exponent--;
while (s2 & mask28)
{
lsr96(s2, s1, s0, q2, q1, q0);
binexp++;
s2 = q2;
s1 = q1;
s0 = q0;
}
}
while (pn->exponent < 0)
{
while (!(s2 & (1 << 31) ) )
{
lsl96(s2, s1, s0, q2, q1, q0);
binexp--;
s2 = q2;
s1 = q1;
s0 = q0;
}
q2 = s2 / 10;
r1 = s2 % 10;
r2 = (s1 >> 8) | (r1 << 24);
q1 = r2 / 10;
r1 = r2 % 10;
r2 = ( (s1 & 0xFF) << 16) | (s0 >> 16) | (r1 << 24);
r0 = r2 / 10;
r1 = r2 % 10;
q1 = (q1 << 8) | ( (r0 & 0x00FF0000) >> 16);
q0 = r0 << 16;
r2 = (s0 & 0xFFFF) | (r1 << 16);
q0 |= r2 / 10;
s2 = q2;
s1 = q1;
s0 = q0;
pn->exponent++;
}
if (s2 || s1 || s0)
{
while (!(s2 & mask28) )
{
lsl96(s2, s1, s0, q2, q1, q0);
binexp--;
s2 = q2;
s1 = q1;
s0 = q0;
}
}
binexp += 1023;
if (binexp > 2046)
{
if (pn->negative)
{
hd.u = DOUBLE_MINUS_INFINITY;
}
else
{
hd.u = DOUBLE_PLUS_INFINITY;
}
}
else if (binexp < 1)
{
if (pn->negative)
{
hd.u = DOUBLE_MINUS_ZERO;
}
}
else if (s2)
{
uint64_t q;
uint64_t binexs2 = (uint64_t)binexp;
binexs2 <<= 52;
q = ( (uint64_t)(s2 & ~mask28) << 24)
| ( ( (uint64_t)s1 + 128) >> 8) | binexs2;
if (pn->negative)
{
q |= (1ULL << 63);
}
hd.u = q;
}
return hd.d;
}
double fromString(const char* _str)
{
struct PrepNumber pn;
union HexDouble hd;
int i;
double result;
pn.mantissa = 0;
pn.negative = 0;
pn.exponent = 0;
hd.u = DOUBLE_PLUS_ZERO;
i = parser(_str, &pn);
switch (i)
{
case PARSER_OK:
result = converter(&pn);
break;
case PARSER_PZERO:
result = hd.d;
break;
case PARSER_MZERO:
hd.u = DOUBLE_MINUS_ZERO;
result = hd.d;
break;
case PARSER_PINF:
hd.u = DOUBLE_PLUS_INFINITY;
result = hd.d;
break;
case PARSER_MINF:
hd.u = DOUBLE_MINUS_INFINITY;
result = hd.d;
break;
}
return result;
}
} // namespace bx

View File

@@ -182,6 +182,49 @@ TEST_CASE("toString double", "")
REQUIRE(testToString(0.0000000001, "1e-10") );
}
static bool testFromString(double _value, const char* _input)
{
char tmp[1024];
int32_t num = bx::toString(tmp, BX_COUNTOF(tmp), _value);
const double lhs = bx::fromString(tmp);
const double rhs = bx::fromString(_input);
if (lhs == rhs)
{
return true;
}
printf("result '%f', input '%s'\n", _value, _input);
return false;
}
TEST_CASE("fromString double", "")
{
REQUIRE(testFromString(0.0, "0.0") );
REQUIRE(testFromString(-0.0, "-0.0") );
REQUIRE(testFromString(1.0, "1.0") );
REQUIRE(testFromString(-1.0, "-1.0") );
REQUIRE(testFromString(1.2345, "1.2345") );
REQUIRE(testFromString(1.2345678, "1.2345678") );
REQUIRE(testFromString(0.123456789012, "0.123456789012") );
REQUIRE(testFromString(1234567.8, "1234567.8") );
REQUIRE(testFromString(-79.39773355813419, "-79.39773355813419") );
REQUIRE(testFromString(0.000001, "0.000001") );
REQUIRE(testFromString(0.0000001, "1e-7") );
REQUIRE(testFromString(1e30, "1e30") );
REQUIRE(testFromString(1.234567890123456e30, "1.234567890123456e30") );
REQUIRE(testFromString(-5e-324, "-5e-324") );
REQUIRE(testFromString(2.225073858507201e-308, "2.225073858507201e-308") );
REQUIRE(testFromString(2.2250738585072014e-308, "2.2250738585072014e-308") );
REQUIRE(testFromString(1.7976931348623157e308, "1.7976931348623157e308") );
REQUIRE(testFromString(0.00000123123123, "0.00000123123123") );
REQUIRE(testFromString(0.000000123123123, "1.23123123e-7") );
REQUIRE(testFromString(123123.123, "123123.123") );
REQUIRE(testFromString(1231231.23, "1231231.23") );
REQUIRE(testFromString(0.000000000123123, "1.23123e-10") );
REQUIRE(testFromString(0.0000000001, "1e-10") );
}
TEST_CASE("StringView", "")
{
bx::StringView sv("test");