diff options
Diffstat (limited to 'src/VBox/Runtime/common/string/strtofloat.cpp')
-rw-r--r-- | src/VBox/Runtime/common/string/strtofloat.cpp | 1400 |
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..363f0821 --- /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-2022 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; +} + |