summaryrefslogtreecommitdiffstats
path: root/src/VBox/Runtime/common/string/strtofloat.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Runtime/common/string/strtofloat.cpp')
-rw-r--r--src/VBox/Runtime/common/string/strtofloat.cpp1400
1 files changed, 1400 insertions, 0 deletions
diff --git a/src/VBox/Runtime/common/string/strtofloat.cpp b/src/VBox/Runtime/common/string/strtofloat.cpp
new file mode 100644
index 00000000..44deafef
--- /dev/null
+++ b/src/VBox/Runtime/common/string/strtofloat.cpp
@@ -0,0 +1,1400 @@
+/* $Id: strtofloat.cpp $ */
+/** @file
+ * IPRT - String To Floating Point Conversion.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/string.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/ctype.h> /* needed for RT_C_IS_DIGIT */
+#include <iprt/err.h>
+
+#include <float.h>
+#include <math.h>
+#if !defined(_MSC_VER) || !defined(IPRT_NO_CRT) /** @todo fix*/
+# include <fenv.h>
+#endif
+#ifndef INFINITY /* Not defined on older Solaris (like the one in the add build VM). */
+# define INFINITY HUGE_VAL
+#endif
+
+#if defined(SOFTFLOAT_FAST_INT64) && !defined(RT_COMPILER_WITH_128BIT_LONG_DOUBLE) /** @todo better softfloat indicator? */
+# define USE_SOFTFLOAT /* for scaling by power of 10 */
+#endif
+#ifdef USE_SOFTFLOAT
+# include <softfloat.h>
+#endif
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+typedef union FLOATUNION
+{
+#ifdef RT_COMPILER_WITH_128BIT_LONG_DOUBLE
+ RTFLOAT128U lrd;
+#elif defined(RT_COMPILER_WITH_80BIT_LONG_DOUBLE)
+ RTFLOAT80U2 lrd;
+#else
+ RTFLOAT64U lrd;
+#endif
+ RTFLOAT64U rd;
+ RTFLOAT32U r;
+} FLOATUNION;
+
+#define RET_TYPE_FLOAT 0
+#define RET_TYPE_DOUBLE 1
+#define RET_TYPE_LONG_DOUBLE 2
+
+#ifdef RT_COMPILER_WITH_128BIT_LONG_DOUBLE
+typedef RTFLOAT128U LONG_DOUBLE_U_T;
+typedef __uint128_t UINT_MANTISSA_T;
+# define UINT_MANTISSA_T_BITS 128
+#elif defined(RT_COMPILER_WITH_80BIT_LONG_DOUBLE)
+typedef RTFLOAT80U2 LONG_DOUBLE_U_T;
+typedef uint64_t UINT_MANTISSA_T;
+# define UINT_MANTISSA_T_BITS 64
+#else
+typedef RTFLOAT64U LONG_DOUBLE_U_T;
+typedef uint64_t UINT_MANTISSA_T;
+# define UINT_MANTISSA_T_BITS 64
+#endif
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/* in strtonum.cpp */
+extern const unsigned char g_auchDigits[256];
+
+#define DIGITS_ZERO_TERM 254
+#define DIGITS_COLON 253
+#define DIGITS_SPACE 252
+#define DIGITS_DOT 251
+
+/** Pair of default float quiet NaN values (indexed by fPositive). */
+static RTFLOAT32U const g_ar32QNan[2] = { RTFLOAT32U_INIT_QNAN(1), RTFLOAT32U_INIT_QNAN(0) };
+
+/** Pair of default double quiet NaN values (indexed by fPositive). */
+static RTFLOAT64U const g_ardQNan[2] = { RTFLOAT64U_INIT_QNAN(1), RTFLOAT64U_INIT_QNAN(0) };
+
+/** Pair of default double quiet NaN values (indexed by fPositive). */
+#if defined(RT_COMPILER_WITH_128BIT_LONG_DOUBLE)
+static RTFLOAT128U const g_alrdQNan[2] = { RTFLOAT128U_INIT_QNAN(1), RTFLOAT128U_INIT_QNAN(0) };
+#elif defined(RT_COMPILER_WITH_80BIT_LONG_DOUBLE)
+static RTFLOAT80U2 const g_alrdQNan[2] = { RTFLOAT80U_INIT_QNAN(1), RTFLOAT80U_INIT_QNAN(0) };
+#else
+static RTFLOAT64U const g_alrdQNan[2] = { RTFLOAT64U_INIT_QNAN(1), RTFLOAT64U_INIT_QNAN(0) };
+#endif
+
+/** NaN fraction value masks. */
+static uint64_t const g_fNanMasks[3] =
+{
+ RT_BIT_64(RTFLOAT32U_FRACTION_BITS - 1) - 1, /* 22=quiet(1) / silent(0) */
+ RT_BIT_64(RTFLOAT64U_FRACTION_BITS - 1) - 1, /* 51=quiet(1) / silent(0) */
+#if defined(RT_COMPILER_WITH_128BIT_LONG_DOUBLE)
+ RT_BIT_64(RTFLOAT128U_FRACTION_BITS - 1 - 64) - 1, /* 111=quiet(1) / silent(0) */
+#elif defined(RT_COMPILER_WITH_80BIT_LONG_DOUBLE)
+ RT_BIT_64(RTFLOAT80U_FRACTION_BITS - 1) - 1, /* bit 63=NaN; bit 62=quiet(1) / silent(0) */
+#else
+ RT_BIT_64(RTFLOAT64U_FRACTION_BITS - 1) - 1,
+#endif
+};
+
+#if 0
+/** Maximum exponent value in the binary representation for a RET_TYPE_XXX. */
+static const int32_t g_iMaxExp[3] =
+{
+ RTFLOAT32U_EXP_MAX - 1 - RTFLOAT32U_EXP_BIAS,
+ RTFLOAT64U_EXP_MAX - 1 - RTFLOAT64U_EXP_BIAS,
+#if defined(RT_COMPILER_WITH_128BIT_LONG_DOUBLE)
+ RTFLOAT128U_EXP_MAX - 1 - RTFLOAT128U_EXP_BIAS,
+#elif defined(RT_COMPILER_WITH_80BIT_LONG_DOUBLE)
+ RTFLOAT80U_EXP_MAX - 1 - RTFLOAT80U_EXP_BIAS,
+#else
+ RTFLOAT64U_EXP_MAX - 1 - RTFLOAT64U_EXP_BIAS,
+#endif
+};
+
+/** Minimum exponent value in the binary representation for a RET_TYPE_XXX. */
+static const int32_t g_iMinExp[3] =
+{
+ 1 - RTFLOAT32U_EXP_BIAS,
+ 1 - RTFLOAT64U_EXP_BIAS,
+#if defined(RT_COMPILER_WITH_128BIT_LONG_DOUBLE)
+ 1 - RTFLOAT128U_EXP_BIAS,
+#elif defined(RT_COMPILER_WITH_80BIT_LONG_DOUBLE)
+ 1 - RTFLOAT80U_EXP_BIAS,
+#else
+ 1 - RTFLOAT64U_EXP_BIAS,
+#endif
+};
+#endif
+
+#if 0 /* unused */
+# if defined(RT_COMPILER_WITH_80BIT_LONG_DOUBLE) || defined(RT_COMPILER_WITH_128BIT_LONG_DOUBLE)
+static const long double g_lrdPowerMin10 = 1e4931L;
+static const long double g_lrdPowerMax10 = 1e4932L;
+# else
+static const long double g_lrdPowerMin10 = 1e307L;
+static const long double g_lrdPowerMax10 = 1e308L;
+# endif
+#endif
+
+#ifdef USE_SOFTFLOAT
+/** SoftFloat: Power of 10 table using 128-bit floating point.
+ *
+ * @code
+ softfloat_state_t SoftState = SOFTFLOAT_STATE_INIT_DEFAULTS();
+ float128_t Power10;
+ ui32_to_f128M(10, &Power10, &SoftState);
+ for (unsigned iBit = 0; iBit < 13; iBit++)
+ {
+ RTAssertMsg2(" { { UINT64_C(%#018RX64), UINT64_C(%#018RX64) } }, %c* 1e%u (%RU64) *%c\n", Power10.v[0], Power10.v[1],
+ '/', RT_BIT_32(iBit), f128M_to_ui64(&Power10, softfloat_round_near_even, false, &SoftState), '/');
+ f128M_mul(&Power10, &Power10, &Power10, &SoftState);
+ }
+ @endcode */
+static const float128_t g_ar128Power10[] =
+{
+ { { UINT64_C(0x0000000000000000), UINT64_C(0x4002400000000000) } }, /* 1e1 (10) */
+ { { UINT64_C(0x0000000000000000), UINT64_C(0x4005900000000000) } }, /* 1e2 (100) */
+ { { UINT64_C(0x0000000000000000), UINT64_C(0x400c388000000000) } }, /* 1e4 (10000) */
+ { { UINT64_C(0x0000000000000000), UINT64_C(0x40197d7840000000) } }, /* 1e8 (100000000) */
+ { { UINT64_C(0x0000000000000000), UINT64_C(0x40341c37937e0800) } }, /* 1e16 (10000000000000000) */
+ { { UINT64_C(0x6b3be04000000000), UINT64_C(0x40693b8b5b5056e1) } }, /* 1e32 (18446744073709551615) */
+ { { UINT64_C(0x4daa797ed6e38ed6), UINT64_C(0x40d384f03e93ff9f) } }, /* 1e64 (18446744073709551615) */
+ { { UINT64_C(0x19bf8cde66d86d61), UINT64_C(0x41a827748f9301d3) } }, /* 1e128 (18446744073709551615) */
+ { { UINT64_C(0xbd1bbb77203731fb), UINT64_C(0x435154fdd7f73bf3) } }, /* 1e256 (18446744073709551615) */
+ { { UINT64_C(0x238d98cab8a97899), UINT64_C(0x46a3c633415d4c1d) } }, /* 1e512 (18446744073709551615) */
+ { { UINT64_C(0x182eca1a7a51e308), UINT64_C(0x4d4892eceb0d02ea) } }, /* 1e1024 (18446744073709551615) */
+ { { UINT64_C(0xbbc94e9a519c651e), UINT64_C(0x5a923d1676bb8a7a) } }, /* 1e2048 (18446744073709551615) */
+ { { UINT64_C(0x2f3592982a7f005a), UINT64_C(0x752588c0a4051441) } }, /* 1e4096 (18446744073709551615) */
+ /* INF */
+};
+
+/** SoftFloat: Initial value for power of 10 scaling.
+ * This deals with the first 32 powers of 10, covering the a full 64-bit
+ * mantissa and a small exponent w/o needing to make use of g_ar128Power10.
+ *
+ * @code
+ softfloat_state_t SoftState = SOFTFLOAT_STATE_INIT_DEFAULTS();
+ float128_t Num10;
+ ui32_to_f128M(10, &Num10, &SoftState);
+ float128_t Power10;
+ ui32_to_f128M(1, &Power10, &SoftState);
+ for (unsigned cTimes = 0; cTimes < 32; cTimes++)
+ {
+ RTAssertMsg2(" { { UINT64_C(%#018RX64), UINT64_C(%#018RX64) } }, %c* 1e%u (%RU64) *%c\n", Power10.v[0], Power10.v[1],
+ '/', cTimes, f128M_to_ui64(&Power10, softfloat_round_near_even, false, &SoftState), '/');
+ f128M_mul(&Power10, &Num10, &Power10, &SoftState);
+ }
+ @endcode */
+static const float128_t g_ar128Power10Initial[] =
+{
+ { { UINT64_C(0x0000000000000000), UINT64_C(0x3fff000000000000) } }, /* 1e0 (1) */
+ { { UINT64_C(0x0000000000000000), UINT64_C(0x4002400000000000) } }, /* 1e1 (10) */
+ { { UINT64_C(0x0000000000000000), UINT64_C(0x4005900000000000) } }, /* 1e2 (100) */
+ { { UINT64_C(0x0000000000000000), UINT64_C(0x4008f40000000000) } }, /* 1e3 (1000) */
+ { { UINT64_C(0x0000000000000000), UINT64_C(0x400c388000000000) } }, /* 1e4 (10000) */
+ { { UINT64_C(0x0000000000000000), UINT64_C(0x400f86a000000000) } }, /* 1e5 (100000) */
+ { { UINT64_C(0x0000000000000000), UINT64_C(0x4012e84800000000) } }, /* 1e6 (1000000) */
+ { { UINT64_C(0x0000000000000000), UINT64_C(0x4016312d00000000) } }, /* 1e7 (10000000) */
+ { { UINT64_C(0x0000000000000000), UINT64_C(0x40197d7840000000) } }, /* 1e8 (100000000) */
+ { { UINT64_C(0x0000000000000000), UINT64_C(0x401cdcd650000000) } }, /* 1e9 (1000000000) */
+ { { UINT64_C(0x0000000000000000), UINT64_C(0x40202a05f2000000) } }, /* 1e10 (10000000000) */
+ { { UINT64_C(0x0000000000000000), UINT64_C(0x402374876e800000) } }, /* 1e11 (100000000000) */
+ { { UINT64_C(0x0000000000000000), UINT64_C(0x4026d1a94a200000) } }, /* 1e12 (1000000000000) */
+ { { UINT64_C(0x0000000000000000), UINT64_C(0x402a2309ce540000) } }, /* 1e13 (10000000000000) */
+ { { UINT64_C(0x0000000000000000), UINT64_C(0x402d6bcc41e90000) } }, /* 1e14 (100000000000000) */
+ { { UINT64_C(0x0000000000000000), UINT64_C(0x4030c6bf52634000) } }, /* 1e15 (1000000000000000) */
+ { { UINT64_C(0x0000000000000000), UINT64_C(0x40341c37937e0800) } }, /* 1e16 (10000000000000000) */
+ { { UINT64_C(0x0000000000000000), UINT64_C(0x40376345785d8a00) } }, /* 1e17 (100000000000000000) */
+ { { UINT64_C(0x0000000000000000), UINT64_C(0x403abc16d674ec80) } }, /* 1e18 (1000000000000000000) */
+ { { UINT64_C(0x0000000000000000), UINT64_C(0x403e158e460913d0) } }, /* 1e19 (10000000000000000000) */
+ { { UINT64_C(0x0000000000000000), UINT64_C(0x40415af1d78b58c4) } }, /* 1e20 (18446744073709551615) */
+ { { UINT64_C(0x0000000000000000), UINT64_C(0x4044b1ae4d6e2ef5) } }, /* 1e21 (18446744073709551615) */
+ { { UINT64_C(0x2000000000000000), UINT64_C(0x40480f0cf064dd59) } }, /* 1e22 (18446744073709551615) */
+ { { UINT64_C(0x6800000000000000), UINT64_C(0x404b52d02c7e14af) } }, /* 1e23 (18446744073709551615) */
+ { { UINT64_C(0x4200000000000000), UINT64_C(0x404ea784379d99db) } }, /* 1e24 (18446744073709551615) */
+ { { UINT64_C(0x0940000000000000), UINT64_C(0x405208b2a2c28029) } }, /* 1e25 (18446744073709551615) */
+ { { UINT64_C(0x4b90000000000000), UINT64_C(0x40554adf4b732033) } }, /* 1e26 (18446744073709551615) */
+ { { UINT64_C(0x1e74000000000000), UINT64_C(0x40589d971e4fe840) } }, /* 1e27 (18446744073709551615) */
+ { { UINT64_C(0x1308800000000000), UINT64_C(0x405c027e72f1f128) } }, /* 1e28 (18446744073709551615) */
+ { { UINT64_C(0x17caa00000000000), UINT64_C(0x405f431e0fae6d72) } }, /* 1e29 (18446744073709551615) */
+ { { UINT64_C(0x9dbd480000000000), UINT64_C(0x406293e5939a08ce) } }, /* 1e30 (18446744073709551615) */
+ { { UINT64_C(0x452c9a0000000000), UINT64_C(0x4065f8def8808b02) } }, /* 1e31 (18446744073709551615) */
+};
+
+#else /* !USE_SOFTFLOAT */
+/** Long Double: Power of 10 table scaling table.
+ * @note LDBL_MAX_10_EXP is 4932 for 80-bit and 308 for 64-bit type. */
+static const long double a_lrdPower10[] =
+{
+ 1e1L,
+ 1e2L,
+ 1e4L,
+ 1e8L,
+ 1e16L,
+ 1e32L,
+ 1e64L,
+ 1e128L,
+ 1e256L,
+# if defined(RT_COMPILER_WITH_80BIT_LONG_DOUBLE) || defined(RT_COMPILER_WITH_128BIT_LONG_DOUBLE)
+ 1e512L,
+ 1e1024L,
+ 1e2048L,
+ 1e4096L,
+# endif
+};
+
+/** Long double: Initial value for power of 10 scaling.
+ * This deals with the first 32 powers of 10, covering the a full 64-bit
+ * mantissa and a small exponent w/o needing to make use of g_ar128Power10. */
+static const long double g_alrdPower10Initial[] =
+{
+ 1e0L,
+ 1e1L,
+ 1e2L,
+ 1e3L,
+ 1e4L,
+ 1e5L,
+ 1e6L,
+ 1e7L,
+ 1e8L,
+ 1e9L,
+ 1e10L,
+ 1e11L,
+ 1e12L,
+ 1e13L,
+ 1e14L,
+ 1e15L,
+ 1e16L,
+ 1e17L,
+ 1e18L,
+ 1e19L,
+ 1e20L,
+ 1e21L,
+ 1e22L,
+ 1e23L,
+ 1e24L,
+ 1e25L,
+ 1e26L,
+ 1e27L,
+ 1e28L,
+ 1e29L,
+ 1e30L,
+ 1e31L,
+};
+
+/* Tell the compiler that we'll mess with the FPU environment. */
+# ifdef _MSC_VER
+# pragma fenv_access(on)
+# endif
+#endif /*!USE_SOFTFLOAT */
+
+
+/**
+ * Multiply @a pVal by 10 to the power of @a iExponent10.
+ *
+ * This is currently a weak point where we might end up with rounding issues.
+ */
+static int rtStrToLongDoubleExp10(LONG_DOUBLE_U_T *pVal, int iExponent10)
+{
+ AssertReturn(iExponent10 != 0, VINF_SUCCESS);
+#ifdef USE_SOFTFLOAT
+ /* Use 128-bit precision floating point from softfloat to improve accuracy. */
+
+ softfloat_state_t SoftState = SOFTFLOAT_STATE_INIT_DEFAULTS();
+ float128_t Val;
+# ifdef RT_COMPILER_WITH_80BIT_LONG_DOUBLE
+ extFloat80M Tmp = EXTFLOAT80M_INIT(pVal->s2.uSignAndExponent, pVal->s2.uMantissa);
+ extF80M_to_f128M(&Tmp, &Val, &SoftState);
+# else
+ float64_t Tmp = { pVal->u };
+ f64_to_f128M(Tmp, &Val, &SoftState);
+# endif
+
+ /*
+ * Calculate the scaling factor. If we need to make use of the last table
+ * entry, we will do part of the scaling here to avoid overflowing Factor.
+ */
+ unsigned uAbsExp = (unsigned)RT_ABS(iExponent10);
+ AssertCompile(RT_ELEMENTS(g_ar128Power10Initial) == 32);
+ unsigned iBit = 5;
+ float128_t Factor = g_ar128Power10Initial[uAbsExp & 31];
+ uAbsExp >>= iBit;
+ while (uAbsExp != 0)
+ {
+ if (iBit < RT_ELEMENTS(g_ar128Power10))
+ {
+ if (uAbsExp & 1)
+ {
+ if (iBit < RT_ELEMENTS(g_ar128Power10) - 1)
+ f128M_mul(&Factor, &g_ar128Power10[iBit], &Factor, &SoftState);
+ else
+ {
+ /* Must do it in two steps to avoid prematurely overflowing the factor value. */
+ if (iExponent10 > 0)
+ f128M_mul(&Val, &Factor, &Val, &SoftState);
+ else
+ f128M_div(&Val, &Factor, &Val, &SoftState);
+ Factor = g_ar128Power10[iBit];
+ }
+ }
+ }
+ else if (iExponent10 < 0)
+ {
+ pVal->r = pVal->r < 0.0L ? -0.0L : +0.0L;
+ return VERR_FLOAT_UNDERFLOW;
+ }
+ else
+ {
+ pVal->r = pVal->r < 0.0L ? -INFINITY : +INFINITY;
+ return VERR_FLOAT_OVERFLOW;
+ }
+ iBit++;
+ uAbsExp >>= 1;
+ }
+
+ /*
+ * Do the scaling (or what remains).
+ */
+ if (iExponent10 > 0)
+ f128M_mul(&Val, &Factor, &Val, &SoftState);
+ else
+ f128M_div(&Val, &Factor, &Val, &SoftState);
+
+# ifdef RT_COMPILER_WITH_80BIT_LONG_DOUBLE
+ f128M_to_extF80M(&Val, &Tmp, &SoftState);
+ pVal->s2.uSignAndExponent = Tmp.signExp;
+ pVal->s2.uMantissa = Tmp.signif;
+# else
+ Tmp = f128M_to_f64(&Val, &SoftState);
+ pVal->u = Tmp.v;
+# endif
+
+ /*
+ * Check for under/overflow and return.
+ */
+ int rc;
+ if (!(SoftState.exceptionFlags & (softfloat_flag_underflow | softfloat_flag_overflow)))
+ rc = VINF_SUCCESS;
+ else if (SoftState.exceptionFlags & softfloat_flag_underflow)
+ rc = VERR_FLOAT_UNDERFLOW;
+ else
+ rc = VERR_FLOAT_OVERFLOW;
+
+#else /* !USE_SOFTFLOAT */
+# if 0
+ /*
+ * Use RTBigNum, falling back on the simple approach if we don't need the
+ * precision or run out of memory?
+ */
+ /** @todo implement RTBigNum approach */
+# endif
+
+ /*
+ * Simple approach.
+ */
+# if !defined(_MSC_VER) || !defined(IPRT_NO_CRT) /** @todo fix*/
+ fenv_t SavedFpuEnv;
+ feholdexcept(&SavedFpuEnv);
+# endif
+
+ /*
+ * Calculate the scaling factor. If we need to make use of the last table
+ * entry, we will do part of the scaling here to avoid overflowing lrdFactor.
+ */
+ AssertCompile(RT_ELEMENTS(g_alrdPower10Initial) == 32);
+ int rc = VINF_SUCCESS;
+ unsigned uAbsExp = (unsigned)RT_ABS(iExponent10);
+ long double lrdFactor = g_alrdPower10Initial[uAbsExp & 31];
+ unsigned iBit = 5;
+ uAbsExp >>= iBit;
+
+ while (uAbsExp != 0)
+ {
+ if (iBit < RT_ELEMENTS(a_lrdPower10))
+ {
+ if (uAbsExp & 1)
+ {
+ if (iBit < RT_ELEMENTS(a_lrdPower10) - 1)
+ lrdFactor *= a_lrdPower10[iBit];
+ else
+ {
+ /* Must do it in two steps to avoid prematurely overflowing the factor value. */
+ if (iExponent10 < 0)
+ pVal->r /= lrdFactor;
+ else
+ pVal->r *= lrdFactor;
+ lrdFactor = a_lrdPower10[iBit];
+ }
+ }
+ }
+ else if (iExponent10 < 0)
+ {
+ pVal->r = pVal->r < 0.0L ? -0.0L : +0.0L;
+ rc = VERR_FLOAT_UNDERFLOW;
+ break;
+ }
+ else
+ {
+ pVal->r = pVal->r < 0.0L ? -INFINITY : +INFINITY;
+ rc = VERR_FLOAT_OVERFLOW;
+ break;
+ }
+ iBit++;
+ uAbsExp >>= 1;
+ }
+
+ /*
+ * Do the scaling (or what remains).
+ */
+ if (iExponent10 < 0)
+ pVal->r /= lrdFactor;
+ else
+ pVal->r *= lrdFactor;
+
+# if !defined(_MSC_VER) || !defined(IPRT_NO_CRT) /** @todo fix*/
+ fesetenv(&SavedFpuEnv);
+# endif
+
+#endif /* !USE_SOFTFLOAT */
+ return rc;
+}
+
+
+
+/**
+ * Set @a ppszNext and check for trailing spaces & chars if @a rc is
+ * VINF_SUCCESS.
+ *
+ * @returns IPRT status code.
+ * @param psz The current input position.
+ * @param ppszNext Where to return the pointer to the end of the value.
+ * Optional.
+ * @param cchMax Number of bytes left in the string starting at @a psz.
+ * @param rc The status code to return.
+ */
+static int rtStrToLongDoubleReturnChecks(const char *psz, char **ppszNext, size_t cchMax, int rc)
+{
+ if (ppszNext)
+ *ppszNext = (char *)psz;
+
+ /* Trailing spaces/chars warning: */
+ if (rc == VINF_SUCCESS && cchMax > 0 && *psz)
+ {
+ do
+ {
+ char ch = *psz++;
+ if (ch == ' ' || ch == '\t')
+ cchMax--;
+ else
+ return ch == '\0' ? VWRN_TRAILING_SPACES : VWRN_TRAILING_CHARS;
+ } while (cchMax > 0);
+ rc = VWRN_TRAILING_SPACES;
+ }
+ return rc;
+}
+
+
+/**
+ * Set @a pRet to infinity, set @a ppszNext, and check for trailing spaces &
+ * chars if @a rc is VINF_SUCCESS.
+ *
+ * @returns IPRT status code.
+ * @param psz The current input position.
+ * @param ppszNext Where to return the pointer to the end of the value.
+ * Optional.
+ * @param cchMax Number of bytes left in the string starting at @a psz.
+ * @param fPositive Whether the infinity should be positive or negative.
+ * @param rc The status code to return.
+ * @param iRetType The target type.
+ * @param pRet Where to store the result.
+ */
+static int rtStrToLongDoubleReturnInf(const char *psz, char **ppszNext, size_t cchMax, bool fPositive,
+ int rc, unsigned iRetType, FLOATUNION *pRet)
+{
+ /*
+ * Skip to the end of long form?
+ */
+ char ch;
+ if ( cchMax >= 5
+ && ((ch = psz[0]) == 'i' || ch == 'I')
+ && ((ch = psz[1]) == 'n' || ch == 'N')
+ && ((ch = psz[2]) == 'i' || ch == 'I')
+ && ((ch = psz[3]) == 't' || ch == 'T')
+ && ((ch = psz[4]) == 'y' || ch == 'Y'))
+ {
+ psz += 5;
+ cchMax -= 5;
+ }
+
+ /*
+ * Set the return value:
+ */
+ switch (iRetType)
+ {
+ case RET_TYPE_FLOAT:
+ {
+ RTFLOAT32U const uRet = RTFLOAT32U_INIT_INF(!fPositive);
+ AssertCompile(sizeof(uRet) == sizeof(pRet->r.r));
+ pRet->r.r = uRet.r;
+ break;
+ }
+
+ case RET_TYPE_LONG_DOUBLE:
+#if defined(RT_COMPILER_WITH_80BIT_LONG_DOUBLE) || defined(RT_COMPILER_WITH_128BIT_LONG_DOUBLE)
+ {
+# if defined(RT_COMPILER_WITH_80BIT_LONG_DOUBLE)
+ RTFLOAT80U2 const uRet = RTFLOAT80U_INIT_INF(!fPositive);
+# else
+ RTFLOAT128U const uRet = RTFLOAT128U_INIT_INF(!fPositive);
+# endif
+ pRet->lrd.lrd = uRet.lrd;
+ break;
+ }
+#else
+ AssertCompile(sizeof(long double) == sizeof(pRet->rd.rd));
+ RT_FALL_THRU();
+#endif
+ case RET_TYPE_DOUBLE:
+ {
+ RTFLOAT64U const uRet = RTFLOAT64U_INIT_INF(!fPositive);
+ AssertCompile(sizeof(uRet) == sizeof(pRet->rd.rd));
+ pRet->rd.rd = uRet.rd;
+ break;
+ }
+
+ default: AssertFailedBreak();
+ }
+
+ /*
+ * Deal with whatever follows and return:
+ */
+ return rtStrToLongDoubleReturnChecks(psz, ppszNext, cchMax, rc);
+}
+
+
+/**
+ * Parses the tag of a "NaN(tag)" value.
+ *
+ * We take the tag to be a number to be put in the mantissa of the NaN, possibly
+ * suffixed by '[_]quiet' or '[_]signaling' (all or part) to indicate the type
+ * of NaN.
+ *
+ * @param pchTag The tag string to parse. Not zero terminated.
+ * @param cchTag The length of the tag string value.
+ * @param fPositive Whether the NaN should be positive or negative.
+ * @param iRetType The target type.
+ * @param pRet Where to store the result.
+ */
+static void rtStrParseNanTag(const char *pchTag, size_t cchTag, bool fPositive, unsigned iRetType, FLOATUNION *pRet)
+{
+ /*
+ * Skip 0x - content is hexadecimal, so this is not necessary.
+ */
+ if (cchTag > 2 && pchTag[0] == '0' && (pchTag[1] == 'x' || pchTag[1] == 'X'))
+ {
+ pchTag += 2;
+ cchTag -= 2;
+ }
+
+ /*
+ * Parse the number, ignoring overflows and stopping on non-xdigit.
+ */
+ uint64_t uHiNum = 0;
+ uint64_t uLoNum = 0;
+ unsigned iXDigit = 0;
+ while (cchTag > 0)
+ {
+ unsigned char uch = (unsigned char)*pchTag;
+ unsigned char uchDigit = g_auchDigits[uch];
+ if (uchDigit >= 16)
+ break;
+ iXDigit++;
+ if (iXDigit >= 16)
+ uHiNum = (uHiNum << 4) | (uLoNum >> 60);
+ uLoNum <<= 4;
+ uLoNum += uchDigit;
+ pchTag++;
+ cchTag--;
+ }
+
+ /*
+ * Check for special "non-standard" quiet / signalling indicator.
+ */
+ while (cchTag > 0 && *pchTag == '_')
+ pchTag++, cchTag--;
+ bool fQuiet = true;
+ if (cchTag > 0)
+ {
+ //const char *pszSkip = NULL;
+ char ch = pchTag[0];
+ if (ch == 'q' || ch == 'Q')
+ {
+ fQuiet = true;
+ //pszSkip = "qQuUiIeEtT\0"; /* cchTag stop before '\0', so we put two at the end to break out of the loop below. */
+ }
+ else if (ch == 's' || ch == 'S')
+ {
+ fQuiet = false;
+ //pszSkip = "sSiIgGnNaAlLiInNgG\0";
+ }
+ //if (pszSkip)
+ // do
+ // {
+ // pchTag++;
+ // cchTag--;
+ // pszSkip += 2;
+ // } while (cchTag > 0 && ((ch = *pchTag) == pszSkip[0] || ch == pszSkip[1]));
+ }
+
+ /*
+ * Adjust the number according to the type.
+ */
+ Assert(iRetType < RT_ELEMENTS(g_fNanMasks));
+#if defined(RT_COMPILER_WITH_128BIT_LONG_DOUBLE)
+ if (iRetType == RET_TYPE_LONG_DOUBLE)
+ uHiNum &= g_fNanMasks[RET_TYPE_LONG_DOUBLE];
+ else
+#endif
+ {
+ uHiNum = 0;
+ uLoNum &= g_fNanMasks[iRetType];
+ }
+ if (!uLoNum && !uHiNum && !fQuiet)
+ uLoNum = 1; /* must not be zero, or it'll turn into an infinity */
+
+ /*
+ * Set the return value.
+ */
+ switch (iRetType)
+ {
+ case RET_TYPE_FLOAT:
+ {
+ RTFLOAT32U const uRet = RTFLOAT32U_INIT_NAN_EX(fQuiet, !fPositive, (uint32_t)uLoNum);
+ pRet->r = uRet;
+ break;
+ }
+
+ case RET_TYPE_LONG_DOUBLE:
+#if defined(RT_COMPILER_WITH_80BIT_LONG_DOUBLE) || defined(RT_COMPILER_WITH_128BIT_LONG_DOUBLE)
+ {
+# if defined(RT_COMPILER_WITH_80BIT_LONG_DOUBLE)
+ RTFLOAT80U2 const uRet = RTFLOAT80U_INIT_NAN_EX(fQuiet, !fPositive, uLoNum);
+# else
+ RTFLOAT128U const uRet = RTFLOAT128U_INIT_NAN_EX(fQuiet, !fPositive, uHiNum, uLoNum);
+# endif
+ pRet->lrd = uRet;
+ break;
+ }
+#else
+ AssertCompile(sizeof(long double) == sizeof(pRet->rd.rd));
+ RT_FALL_THRU();
+#endif
+ case RET_TYPE_DOUBLE:
+ {
+ RTFLOAT64U const uRet = RTFLOAT64U_INIT_NAN_EX(fQuiet, !fPositive, uLoNum);
+ pRet->rd = uRet;
+ break;
+ }
+
+ default: AssertFailedBreak();
+ }
+
+ //return cchTag == 0;
+}
+
+
+/**
+ * Finish parsing NaN, set @a pRet to NaN, set @a ppszNext, and check for
+ * trailing spaces & chars if @a rc is VINF_SUCCESS.
+ *
+ * @returns IPRT status code.
+ * @param psz The current input position.
+ * @param ppszNext Where to return the pointer to the end of the value.
+ * Optional.
+ * @param cchMax Number of bytes left in the string starting at @a psz.
+ * @param fPositive Whether the NaN should be positive or negative.
+ * @param iRetType The target type.
+ * @param pRet Where to store the result.
+ */
+static int rtStrToLongDoubleReturnNan(const char *psz, char **ppszNext, size_t cchMax, bool fPositive,
+ unsigned iRetType, FLOATUNION *pRet)
+{
+ /*
+ * Any NaN sub-number? E.g. NaN(1) or Nan(0x42). We'll require a closing
+ * parenthesis or we'll just ignore it.
+ */
+ if (cchMax >= 2 && *psz == '(')
+ {
+ unsigned cchTag = 1;
+ char ch = '\0';
+ while (cchTag < cchMax && (RT_C_IS_ALNUM((ch = psz[cchTag])) || ch == '_'))
+ cchTag++;
+ if (ch == ')')
+ {
+ rtStrParseNanTag(psz + 1, cchTag - 1, fPositive, iRetType, pRet);
+ psz += cchTag + 1;
+ cchMax -= cchTag + 1;
+ return rtStrToLongDoubleReturnChecks(psz, ppszNext, cchMax, VINF_SUCCESS);
+ }
+ }
+
+ /*
+ * Set the return value to the default NaN value.
+ */
+ switch (iRetType)
+ {
+ case RET_TYPE_FLOAT:
+ pRet->r = g_ar32QNan[fPositive];
+ break;
+
+ case RET_TYPE_DOUBLE:
+ pRet->rd = g_ardQNan[fPositive];
+ break;
+
+ case RET_TYPE_LONG_DOUBLE:
+ pRet->lrd = g_alrdQNan[fPositive];
+ break;
+
+ default: AssertFailedBreak();
+ }
+
+ return rtStrToLongDoubleReturnChecks(psz, ppszNext, cchMax, VINF_SUCCESS);
+}
+
+
+RTDECL(long double) RTStrNanLongDouble(const char *pszTag, bool fPositive)
+{
+ if (pszTag)
+ {
+ size_t cchTag = strlen(pszTag);
+ if (cchTag > 0)
+ {
+ FLOATUNION u;
+ rtStrParseNanTag(pszTag, cchTag, fPositive, RET_TYPE_LONG_DOUBLE, &u);
+ return u.lrd.r;
+ }
+ }
+ return g_alrdQNan[fPositive].r;
+}
+
+
+RTDECL(double) RTStrNanDouble(const char *pszTag, bool fPositive)
+{
+ if (pszTag)
+ {
+ size_t cchTag = strlen(pszTag);
+ if (cchTag > 0)
+ {
+ FLOATUNION u;
+ rtStrParseNanTag(pszTag, cchTag, fPositive, RET_TYPE_DOUBLE, &u);
+ return u.rd.r;
+ }
+ }
+ return g_ardQNan[fPositive].r;
+}
+
+
+RTDECL(float) RTStrNanFloat(const char *pszTag, bool fPositive)
+{
+ if (pszTag)
+ {
+ size_t cchTag = strlen(pszTag);
+ if (cchTag > 0)
+ {
+ FLOATUNION u;
+ rtStrParseNanTag(pszTag, cchTag, fPositive, RET_TYPE_FLOAT, &u);
+ return u.r.r;
+ }
+ }
+ return g_ar32QNan[fPositive].r;
+}
+
+
+/**
+ * Set @a pRet to zero, set @a ppszNext, and check for trailing spaces &
+ * chars if @a rc is VINF_SUCCESS.
+ *
+ * @returns IPRT status code.
+ * @param psz The current input position.
+ * @param ppszNext Where to return the pointer to the end of the value.
+ * Optional.
+ * @param cchMax Number of bytes left in the string starting at @a psz.
+ * @param fPositive Whether the value should be positive or negative.
+ * @param rc The status code to return.
+ * @param iRetType The target type.
+ * @param pRet Where to store the result.
+ */
+static int rtStrToLongDoubleReturnZero(const char *psz, char **ppszNext, size_t cchMax, bool fPositive,
+ int rc, unsigned iRetType, FLOATUNION *pRet)
+{
+ switch (iRetType)
+ {
+ case RET_TYPE_FLOAT:
+ pRet->r.r = fPositive ? +0.0F : -0.0F;
+ break;
+
+ case RET_TYPE_LONG_DOUBLE:
+#if defined(RT_COMPILER_WITH_80BIT_LONG_DOUBLE) || defined(RT_COMPILER_WITH_128BIT_LONG_DOUBLE)
+ pRet->lrd.lrd = fPositive ? +0.0L : -0.0L;
+ break;
+#else
+ AssertCompile(sizeof(long double) == sizeof(pRet->rd.rd));
+ RT_FALL_THRU();
+#endif
+ case RET_TYPE_DOUBLE:
+ pRet->rd.rd = fPositive ? +0.0 : -0.0;
+ break;
+
+ default: AssertFailedBreak();
+ }
+
+ return rtStrToLongDoubleReturnChecks(psz, ppszNext, cchMax, rc);
+}
+
+
+/**
+ * Return overflow or underflow - setting @a pRet and @a ppszNext accordingly.
+ *
+ * @returns IPRT status code.
+ * @param psz The current input position.
+ * @param ppszNext Where to return the pointer to the end of the value.
+ * Optional.
+ * @param cchMax Number of bytes left in the string starting at @a psz.
+ * @param fPositive Whether the value should be positive or negative.
+ * @param iExponent Overflow/underflow indicator.
+ * @param iRetType The target type.
+ * @param pRet Where to store the result.
+ */
+static int rtStrToLongDoubleReturnOverflow(const char *psz, char **ppszNext, size_t cchMax, bool fPositive,
+ int32_t iExponent, unsigned iRetType, FLOATUNION *pRet)
+{
+ if (iExponent > 0)
+ return rtStrToLongDoubleReturnInf(psz, ppszNext, cchMax, fPositive, VERR_FLOAT_OVERFLOW, iRetType, pRet);
+ return rtStrToLongDoubleReturnZero(psz, ppszNext, cchMax, fPositive, VERR_FLOAT_UNDERFLOW, iRetType, pRet);
+}
+
+
+/**
+ * Returns a denormal/subnormal value.
+ *
+ * This implies that iRetType is long double, or double if they are the same,
+ * and that we should warn about underflowing.
+ */
+static int rtStrToLongDoubleReturnSubnormal(const char *psz, char **ppszNext, size_t cchMax, LONG_DOUBLE_U_T const *pVal,
+ unsigned iRetType, FLOATUNION *pRet)
+{
+#if defined(RT_COMPILER_WITH_80BIT_LONG_DOUBLE) || defined(RT_COMPILER_WITH_128BIT_LONG_DOUBLE)
+ Assert(iRetType == RET_TYPE_LONG_DOUBLE);
+ pRet->lrd = *pVal;
+#else
+ Assert(iRetType == RET_TYPE_LONG_DOUBLE || iRetType == RET_TYPE_DOUBLE);
+ pRet->rd = *pVal;
+#endif
+ RT_NOREF(iRetType);
+ return rtStrToLongDoubleReturnChecks(psz, ppszNext, cchMax, VWRN_FLOAT_UNDERFLOW);
+}
+
+
+/**
+ * Packs the given sign, mantissa, and (power of 2) exponent into the
+ * return value.
+ */
+static int rtStrToLongDoubleReturnValue(const char *psz, char **ppszNext, size_t cchMax,
+ bool fPositive, UINT_MANTISSA_T uMantissa, int32_t iExponent,
+ unsigned iRetType, FLOATUNION *pRet)
+{
+ int rc = VINF_SUCCESS;
+ switch (iRetType)
+ {
+ case RET_TYPE_FLOAT:
+ iExponent += RTFLOAT32U_EXP_BIAS;
+ if (iExponent <= 0)
+ {
+ /* Produce a subnormal value if it's within range, otherwise return zero. */
+ if (iExponent < -RTFLOAT32U_FRACTION_BITS)
+ return rtStrToLongDoubleReturnZero(psz, ppszNext, cchMax, fPositive, VERR_FLOAT_UNDERFLOW, iRetType, pRet);
+ rc = VWRN_FLOAT_UNDERFLOW;
+ uMantissa >>= -iExponent + 1;
+ iExponent = 0;
+ }
+ else if (iExponent >= RTFLOAT32U_EXP_MAX)
+ return rtStrToLongDoubleReturnInf(psz, ppszNext, cchMax, fPositive, VERR_FLOAT_OVERFLOW, iRetType, pRet);
+
+ pRet->r.s.uFraction = (uMantissa >> (UINT_MANTISSA_T_BITS - 1 - RTFLOAT32U_FRACTION_BITS))
+ & (RT_BIT_32(RTFLOAT32U_FRACTION_BITS) - 1);
+ pRet->r.s.uExponent = iExponent;
+ pRet->r.s.fSign = !fPositive;
+ break;
+
+ case RET_TYPE_LONG_DOUBLE:
+#ifdef RT_COMPILER_WITH_80BIT_LONG_DOUBLE
+# if UINT_MANTISSA_T_BITS != 64
+# error Unsupported UINT_MANTISSA_T_BITS count.
+# endif
+ iExponent += RTFLOAT80U_EXP_BIAS;
+ if (iExponent <= 0)
+ {
+ /* Produce a subnormal value if it's within range, otherwise return zero. */
+ if (iExponent < -RTFLOAT80U_FRACTION_BITS)
+ return rtStrToLongDoubleReturnZero(psz, ppszNext, cchMax, fPositive, VERR_FLOAT_UNDERFLOW, iRetType, pRet);
+ rc = VWRN_FLOAT_UNDERFLOW;
+ uMantissa >>= -iExponent + 1;
+ iExponent = 0;
+ }
+ else if (iExponent >= RTFLOAT80U_EXP_MAX)
+ return rtStrToLongDoubleReturnInf(psz, ppszNext, cchMax, fPositive, VERR_FLOAT_OVERFLOW, iRetType, pRet);
+
+ pRet->lrd.s.uMantissa = uMantissa;
+ pRet->lrd.s.uExponent = iExponent;
+ pRet->lrd.s.fSign = !fPositive;
+ break;
+#elif defined(RT_COMPILER_WITH_128BIT_LONG_DOUBLE)
+ iExponent += RTFLOAT128U_EXP_BIAS;
+ uMantissa >>= 128 - RTFLOAT128U_FRACTION_BITS;
+ if (iExponent <= 0)
+ {
+ /* Produce a subnormal value if it's within range, otherwise return zero. */
+ if (iExponent < -RTFLOAT128U_FRACTION_BITS)
+ return rtStrToLongDoubleReturnZero(psz, ppszNext, cchMax, fPositive, VERR_FLOAT_UNDERFLOW, iRetType, pRet);
+ rc = VWRN_FLOAT_UNDERFLOW;
+ uMantissa >>= -iExponent + 1;
+ iExponent = 0;
+ }
+ else if (iExponent >= RTFLOAT80U_EXP_MAX)
+ return rtStrToLongDoubleReturnInf(psz, ppszNext, cchMax, fPositive, VERR_FLOAT_OVERFLOW, iRetType, pRet);
+ pRet->lrd.s64.uFractionHi = (uint64_t)(uMantissa >> 64) & (RT_BIT_64(RTFLOAT128U_FRACTION_BITS - 64) - 1);
+ pRet->lrd.s64.uFractionLo = (uint64_t)uMantissa;
+ pRet->lrd.s64.uExponent = iExponent;
+ pRet->lrd.s64.fSign = !fPositive;
+ break;
+#else
+ AssertCompile(sizeof(long double) == sizeof(pRet->rd.rd));
+ RT_FALL_THRU();
+#endif
+ case RET_TYPE_DOUBLE:
+ iExponent += RTFLOAT64U_EXP_BIAS;
+ if (iExponent <= 0)
+ {
+ /* Produce a subnormal value if it's within range, otherwise return zero. */
+ if (iExponent < -RTFLOAT64U_FRACTION_BITS)
+ return rtStrToLongDoubleReturnZero(psz, ppszNext, cchMax, fPositive, VERR_FLOAT_UNDERFLOW, iRetType, pRet);
+ rc = VWRN_FLOAT_UNDERFLOW;
+ uMantissa >>= -iExponent + 1;
+ iExponent = 0;
+ }
+ else if (iExponent >= RTFLOAT64U_EXP_MAX)
+ return rtStrToLongDoubleReturnInf(psz, ppszNext, cchMax, fPositive, VERR_FLOAT_OVERFLOW, iRetType, pRet);
+
+ pRet->rd.s64.uFraction = (uMantissa >> (UINT_MANTISSA_T_BITS - 1 - RTFLOAT64U_FRACTION_BITS))
+ & (RT_BIT_64(RTFLOAT64U_FRACTION_BITS) - 1);
+ pRet->rd.s64.uExponent = iExponent;
+ pRet->rd.s64.fSign = !fPositive;
+ break;
+
+ default:
+ AssertFailedReturn(VERR_INTERNAL_ERROR_3);
+ }
+ return rtStrToLongDoubleReturnChecks(psz, ppszNext, cchMax, rc);
+}
+
+
+/**
+ * Worker for RTStrToLongDoubleEx, RTStrToDoubleEx and RTStrToFloatEx.
+ *
+ * @returns IPRT status code
+ * @param pszValue The string value to convert.
+ * @param ppszNext Where to return the pointer to the end of the value.
+ * Optional.
+ * @param cchMax Number of bytes left in the string starting at @a psz.
+ * @param iRetType The return type: float, double or long double.
+ * @param pRet The return value union.
+ */
+static int rtStrToLongDoubleWorker(const char *pszValue, char **ppszNext, size_t cchMax, unsigned iRetType, FLOATUNION *pRet)
+{
+ const char *psz = pszValue;
+ if (!cchMax)
+ cchMax = ~(size_t)cchMax;
+
+ /*
+ * Sign.
+ */
+ bool fPositive = true;
+ while (cchMax > 0)
+ {
+ if (*psz == '+')
+ fPositive = true;
+ else if (*psz == '-')
+ fPositive = !fPositive;
+ else
+ break;
+ psz++;
+ cchMax--;
+ }
+
+ /*
+ * Constant like "Inf", "Infinity", "NaN" or "NaN(hexstr)"?
+ */
+ /* "Inf" or "Infinity"? */
+ if (cchMax == 0)
+ return rtStrToLongDoubleReturnZero(pszValue, ppszNext, cchMax, fPositive, VERR_NO_DIGITS, iRetType, pRet);
+ if (cchMax >= 3)
+ {
+ char ch = *psz;
+ /* Inf: */
+ if (ch == 'i' || ch == 'I')
+ {
+ if ( ((ch = psz[1]) == 'n' || ch == 'N')
+ && ((ch = psz[2]) == 'f' || ch == 'F'))
+ return rtStrToLongDoubleReturnInf(psz + 3, ppszNext, cchMax - 3, fPositive, VINF_SUCCESS, iRetType, pRet);
+ }
+ /* Nan: */
+ else if (ch == 'n' || ch == 'N')
+ {
+ if ( ((ch = psz[1]) == 'a' || ch == 'A')
+ && ((ch = psz[2]) == 'n' || ch == 'N'))
+ return rtStrToLongDoubleReturnNan(psz + 3, ppszNext, cchMax - 3, fPositive, iRetType, pRet);
+ }
+ }
+
+ /*
+ * Check for hex prefix.
+ */
+#ifdef RT_COMPILER_WITH_128BIT_LONG_DOUBLE
+ unsigned cMaxDigits = 33;
+#elif defined(RT_COMPILER_WITH_80BIT_LONG_DOUBLE)
+ unsigned cMaxDigits = 19;
+#else
+ unsigned cMaxDigits = 18;
+#endif
+ unsigned uBase = 10;
+ unsigned uExpDigitFactor = 1;
+ if (cchMax >= 2 && psz[0] == '0' && (psz[1] == 'x' || psz[1] == 'X'))
+ {
+ cMaxDigits = 16;
+ uBase = 16;
+ uExpDigitFactor = 4;
+ cchMax -= 2;
+ psz += 2;
+ }
+
+ /*
+ * Now, parse the mantissa.
+ */
+#ifdef RT_COMPILER_WITH_128BIT_LONG_DOUBLE
+ uint8_t abDigits[36];
+#else
+ uint8_t abDigits[20];
+#endif
+ unsigned cDigits = 0;
+ unsigned cFractionDigits = 0;
+ uint8_t fSeenNonZeroDigit = 0;
+ bool fInFraction = false;
+ bool fSeenDigits = false;
+ while (cchMax > 0)
+ {
+ uint8_t b = g_auchDigits[(unsigned char)*psz];
+ if (b < uBase)
+ {
+ fSeenDigits = true;
+ fSeenNonZeroDigit |= b;
+ if (fSeenNonZeroDigit)
+ {
+ if (cDigits < RT_ELEMENTS(abDigits))
+ abDigits[cDigits] = b;
+ cDigits++;
+ cFractionDigits += fInFraction;
+ }
+ }
+ else if (b == DIGITS_DOT && !fInFraction)
+ fInFraction = true;
+ else
+ break;
+ psz++;
+ cchMax--;
+ }
+
+ /* If we've seen no digits, or just a dot, return zero already. */
+ if (!fSeenDigits)
+ {
+ if (fInFraction) /* '+.' => 0.0 ? */
+ return rtStrToLongDoubleReturnZero(psz, ppszNext, cchMax, fPositive, VINF_SUCCESS, iRetType, pRet);
+ if (uBase == 16) /* '+0x' => 0.0 & *=pszNext="x..." */
+ return rtStrToLongDoubleReturnZero(psz - 1, ppszNext, cchMax, fPositive, VINF_SUCCESS, iRetType, pRet);
+ /* '' and '+' -> no digits + 0.0. */
+ return rtStrToLongDoubleReturnZero(pszValue, ppszNext, cchMax, fPositive, VERR_NO_DIGITS, iRetType, pRet);
+ }
+
+ /*
+ * Parse the exponent.
+ * This is optional and we ignore incomplete ones like "e+".
+ */
+ int32_t iExponent = 0;
+ if (cchMax >= 2) /* min "e0" */
+ {
+ char ch = *psz;
+ if (uBase == 10 ? ch == 'e' || ch == 'E' : ch == 'p' || ch == 'P')
+ {
+ bool fExpOverflow = false;
+ bool fPositiveExp = true;
+ size_t off = 1;
+ ch = psz[off];
+ if (ch == '+' || ch == '-')
+ {
+ fPositiveExp = ch == '+';
+ off++;
+ }
+ uint8_t b;
+ if ( off < cchMax
+ && (b = g_auchDigits[(unsigned char)psz[off]]) < 10)
+ {
+ do
+ {
+ int32_t const iPreviousExponent = iExponent;
+ iExponent *= 10;
+ iExponent += b;
+ if (iExponent < iPreviousExponent)
+ fExpOverflow = true;
+ off++;
+ } while (off < cchMax && (b = g_auchDigits[(unsigned char)psz[off]]) < 10);
+ if (!fPositiveExp)
+ iExponent = -iExponent;
+ cchMax -= off;
+ psz += off;
+ }
+ if (fExpOverflow || iExponent <= -65536 || iExponent >= 65536)
+ return rtStrToLongDoubleReturnOverflow(pszValue, ppszNext, cchMax, fPositive, iExponent, iRetType, pRet);
+ }
+ }
+
+ /* If the mantissa was all zeros, we can return zero now that we're past the exponent. */
+ if (!fSeenNonZeroDigit)
+ return rtStrToLongDoubleReturnZero(psz, ppszNext, cchMax, fPositive, VINF_SUCCESS, iRetType, pRet);
+
+ /*
+ * Adjust the expontent so we've got all digits to the left of the decimal point.
+ */
+ iExponent -= cFractionDigits * uExpDigitFactor;
+
+ /*
+ * Drop digits we won't translate.
+ */
+ if (cDigits > cMaxDigits)
+ {
+ iExponent += (cDigits - cMaxDigits) * uExpDigitFactor;
+ cDigits = cMaxDigits;
+ }
+
+ /*
+ * Strip least significant zero digits.
+ */
+ while (cDigits > 0 && abDigits[cDigits - 1] == 0)
+ {
+ cDigits--;
+ iExponent += uExpDigitFactor;
+ }
+
+ /*
+ * The hexadecimal is relatively straight forward.
+ */
+ if (uBase == 16)
+ {
+ UINT_MANTISSA_T uMantissa = 0;
+ for (unsigned iDigit = 0; iDigit < cDigits; iDigit++)
+ {
+ uMantissa |= (UINT_MANTISSA_T)abDigits[iDigit] << (UINT_MANTISSA_T_BITS - 4 - iDigit * 4);
+ iExponent += 4;
+ }
+ Assert(uMantissa != 0);
+
+ /* Shift to the left till the most significant bit is 1. */
+ if (!((uMantissa >> (UINT_MANTISSA_T_BITS - 1)) & 1))
+ {
+#if UINT_MANTISSA_T_BITS == 64
+ unsigned cShift = 64 - ASMBitLastSetU64(uMantissa);
+ uMantissa <<= cShift;
+ iExponent -= cShift;
+ Assert(uMantissa & RT_BIT_64(63));
+#else
+ do
+ {
+ uMantissa <<= 1;
+ iExponent -= 1;
+ } while (!((uMantissa >> (UINT_MANTISSA_T_BITS - 1)) & 1));
+#endif
+ }
+
+ /* Account for the 1 left of the decimal point. */
+ iExponent--;
+
+ /*
+ * Produce the return value.
+ */
+ return rtStrToLongDoubleReturnValue(psz, ppszNext, cchMax, fPositive, uMantissa, iExponent, iRetType, pRet);
+ }
+
+ /*
+ * For the decimal format, we'll rely on the floating point conversion of
+ * the compiler/CPU for the mantissa.
+ */
+ UINT_MANTISSA_T uMantissa = 0;
+ for (unsigned iDigit = 0; iDigit < cDigits; iDigit++)
+ {
+ uMantissa *= 10;
+ uMantissa += abDigits[iDigit];
+ }
+ Assert(uMantissa != 0);
+
+ LONG_DOUBLE_U_T uTmp;
+ uTmp.r = fPositive ? (long double)uMantissa : -(long double)uMantissa;
+
+ /*
+ * Here comes the fun part, scaling it according to the power of 10 exponent.
+ * We only need to consider overflows and underflows when scaling, when
+ * iExponent is zero we can be sure the target type can handle the result.
+ */
+ if (iExponent != 0)
+ {
+ rtStrToLongDoubleExp10(&uTmp, iExponent);
+#ifdef RT_COMPILER_WITH_80BIT_LONG_DOUBLE
+ if (!RTFLOAT80U_IS_NORMAL(&uTmp))
+#elif defined(RT_COMPILER_WITH_128BIT_LONG_DOUBLE)
+ if (!RTFLOAT128U_IS_NORMAL(&uTmp))
+#else
+ if (!RTFLOAT64U_IS_NORMAL(&uTmp))
+#endif
+ {
+#ifdef RT_COMPILER_WITH_80BIT_LONG_DOUBLE
+ if (RTFLOAT80U_IS_DENORMAL(&uTmp) && iRetType == RET_TYPE_LONG_DOUBLE)
+#elif defined(RT_COMPILER_WITH_128BIT_LONG_DOUBLE)
+ if (RTFLOAT128U_IS_SUBNORMAL(&uTmp) && iRetType == RET_TYPE_LONG_DOUBLE)
+#else
+ if (RTFLOAT64U_IS_SUBNORMAL(&uTmp) && iRetType != RET_TYPE_FLOAT)
+#endif
+ return rtStrToLongDoubleReturnSubnormal(psz, ppszNext, cchMax, &uTmp, iRetType, pRet);
+ return rtStrToLongDoubleReturnOverflow(psz, ppszNext, cchMax, fPositive, iExponent, iRetType, pRet);
+ }
+ }
+
+ /*
+ * We've got a normal value in uTmp when we get here, just repack it in the
+ * target format and return.
+ */
+#ifdef RT_COMPILER_WITH_80BIT_LONG_DOUBLE
+ Assert(RTFLOAT80U_IS_NORMAL(&uTmp));
+ if (iRetType == RET_TYPE_LONG_DOUBLE)
+ {
+ pRet->lrd = uTmp;
+ return rtStrToLongDoubleReturnChecks(psz, ppszNext, cchMax, VINF_SUCCESS);
+ }
+ fPositive = uTmp.s.fSign;
+ iExponent = uTmp.s.uExponent - RTFLOAT80U_EXP_BIAS;
+ uMantissa = uTmp.s.uMantissa;
+# if UINT_MANTISSA_T_BITS > 64
+ uMantissa <<= UINT_MANTISSA_T_BITS - 64;
+# endif
+#elif defined(RT_COMPILER_WITH_128BIT_LONG_DOUBLE)
+ Assert(RTFLOAT128U_IS_NORMAL(&uTmp));
+ if (iRetType == RET_TYPE_LONG_DOUBLE)
+ {
+ pRet->lrd = uTmp;
+ return rtStrToLongDoubleReturnChecks(psz, ppszNext, cchMax, VINF_SUCCESS);
+ }
+ fPositive = uTmp.s64.fSign;
+ iExponent = uTmp.s64.uExponent - RTFLOAT128U_EXP_BIAS;
+ uMantissa = (UINT_MANTISSA_T)uTmp.s64.uFractionHi << (UINT_MANTISSA_T_BITS - RTFLOAT128U_FRACTION_BITS - 1 + 64);
+ uMantissa |= (UINT_MANTISSA_T)uTmp.s64.uFractionLo << (UINT_MANTISSA_T_BITS - RTFLOAT128U_FRACTION_BITS - 1);
+ uMantissa |= (UINT_MANTISSA_T)1 << (UINT_MANTISSA_T_BITS - 1);
+#else
+ Assert(RTFLOAT64U_IS_NORMAL(&uTmp));
+ if ( iRetType == RET_TYPE_DOUBLE
+ || iRetType == RET_TYPE_LONG_DOUBLE)
+ {
+ pRet->rd = uTmp;
+ return rtStrToLongDoubleReturnChecks(psz, ppszNext, cchMax, VINF_SUCCESS);
+ }
+ fPositive = uTmp.s64.fSign;
+ iExponent = uTmp.s64.uExponent - RTFLOAT64U_EXP_BIAS;
+ uMantissa = uTmp.s64.uFraction | RT_BIT_64(RTFLOAT64U_FRACTION_BITS);
+# if UINT_MANTISSA_T_BITS > 64
+ uMantissa <<= UINT_MANTISSA_T_BITS - 64;
+# endif
+#endif
+ return rtStrToLongDoubleReturnValue(psz, ppszNext, cchMax, fPositive, uMantissa, iExponent, iRetType, pRet);
+}
+
+
+RTDECL(int) RTStrToLongDoubleEx(const char *pszValue, char **ppszNext, size_t cchMax, long double *plrd)
+{
+ FLOATUNION u;
+ int rc = rtStrToLongDoubleWorker(pszValue, ppszNext, cchMax, RET_TYPE_LONG_DOUBLE, &u);
+ if (plrd)
+#ifdef RT_COMPILER_WITH_80BIT_LONG_DOUBLE
+ *plrd = u.lrd.lrd;
+#else
+ *plrd = u.rd.rd;
+#endif
+ return rc;
+}
+
+
+RTDECL(int) RTStrToDoubleEx(const char *pszValue, char **ppszNext, size_t cchMax, double *prd)
+{
+ FLOATUNION u;
+ int rc = rtStrToLongDoubleWorker(pszValue, ppszNext, cchMax, RET_TYPE_DOUBLE, &u);
+ if (prd)
+ *prd = u.rd.rd;
+ return rc;
+}
+
+
+RTDECL(int) RTStrToFloatEx(const char *pszValue, char **ppszNext, size_t cchMax, float *pr)
+{
+ FLOATUNION u;
+ int rc = rtStrToLongDoubleWorker(pszValue, ppszNext, cchMax, RET_TYPE_FLOAT, &u);
+ if (pr)
+ *pr = u.r.r;
+ return rc;
+}
+