Bläddra i källkod

Added fromString.

Branimir Karadžić 8 år sedan
förälder
incheckning
65eb5d12d4
3 ändrade filer med 577 tillägg och 8 borttagningar
  1. 3 0
      include/bx/string.h
  2. 531 8
      src/dtoa.cpp
  3. 43 0
      tests/string_test.cpp

+ 3 - 0
include/bx/string.h

@@ -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"

+ 531 - 8
src/dtoa.cpp

@@ -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

+ 43 - 0
tests/string_test.cpp

@@ -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");