/* $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 . * * 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 #include "internal/iprt.h" #include #include #include /* needed for RT_C_IS_DIGIT */ #include #include #include #if !defined(_MSC_VER) || !defined(IPRT_NO_CRT) /** @todo fix*/ # include #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 #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; }