sal/qa/rtl/math/test-rtl-math.cxx | 6 ++++++ sal/rtl/math.cxx | 33 +++++++++++++++++++++------------ 2 files changed, 27 insertions(+), 12 deletions(-)
New commits: commit a3aaa11db039675252fe9d5bc4a9d43be34940e8 Author: Mike Kaganski <[email protected]> AuthorDate: Wed Sep 17 13:15:15 2025 +0500 Commit: Mike Kaganski <[email protected]> CommitDate: Wed Sep 17 15:15:27 2025 +0200 tdf#168423: Allow subnormals from strtod dtoa sets ERANGE with subnormals. We need to support these, so check for infinity and zero explicitly, and allow other results. NaNs can't appear here. Change-Id: I006a42ebff03b07d219b423c13bf8230348e1def Reviewed-on: https://gerrit.libreoffice.org/c/core/+/191071 Tested-by: Jenkins Reviewed-by: Mike Kaganski <[email protected]> diff --git a/sal/qa/rtl/math/test-rtl-math.cxx b/sal/qa/rtl/math/test-rtl-math.cxx index 53b6827c818b..22714fdd4592 100644 --- a/sal/qa/rtl/math/test-rtl-math.cxx +++ b/sal/qa/rtl/math/test-rtl-math.cxx @@ -234,6 +234,12 @@ public: CPPUNIT_ASSERT_EQUAL(rtl_math_ConversionStatus_Ok, status); CPPUNIT_ASSERT_EQUAL(sal_Int32(23), end); CPPUNIT_ASSERT_EQUAL(fValAfter, res); + + // Minimal subnormal value, as documented for Basic + res = rtl::math::stringToDouble("4.94065645841247E-324", '.', ',', &status, &end); + CPPUNIT_ASSERT_EQUAL(rtl_math_ConversionStatus_Ok, status); + CPPUNIT_ASSERT_EQUAL(sal_Int32(21), end); + CPPUNIT_ASSERT_EQUAL(0x1p-1074, res); } void test_stringToDouble_bad() { diff --git a/sal/rtl/math.cxx b/sal/rtl/math.cxx index 26b539a9cc8c..dbeef855e9c5 100644 --- a/sal/rtl/math.cxx +++ b/sal/rtl/math.cxx @@ -372,23 +372,32 @@ double stringToDouble(CharT const* pBegin, CharT const* pEnd, CharT cDecSeparato fVal = strtod_nolocale(buf, &pCharParseEnd); if (errno == ERANGE) { - // Check for the dreaded rounded to 15 digits max value - // 1.79769313486232e+308 for 1.7976931348623157e+308 we wrote - // everywhere, accept with or without plus sign in exponent. - const char* b = buf; - if (b[0] == '-') - ++b; - if (((pCharParseEnd - b == 21) || (pCharParseEnd - b == 20)) - && !strncmp(b, "1.79769313486232", 16) && (b[16] == 'e' || b[16] == 'E') - && (((pCharParseEnd - b == 21) && !strncmp(b + 17, "+308", 4)) - || ((pCharParseEnd - b == 20) && !strncmp(b + 17, "308", 3)))) + // This happens with overflow and underflow (including subnormals!) + if (std::isinf(fVal)) { - fVal = (buf < b) ? -DBL_MAX : DBL_MAX; + // Check for the dreaded rounded to 15 digits max value + // 1.79769313486232e+308 for 1.7976931348623157e+308 we wrote + // everywhere, accept with or without plus sign in exponent. + const char* b = buf; + if (b[0] == '-') + ++b; + if (((pCharParseEnd - b == 21) || (pCharParseEnd - b == 20)) + && !strncmp(b, "1.79769313486232", 16) && (b[16] == 'e' || b[16] == 'E') + && (((pCharParseEnd - b == 21) && !strncmp(b + 17, "+308", 4)) + || ((pCharParseEnd - b == 20) && !strncmp(b + 17, "308", 3)))) + { + fVal = (buf < b) ? -DBL_MAX : DBL_MAX; + } + else + { + eStatus = rtl_math_ConversionStatus_OutOfRange; + } } - else + else if (fVal == 0) { eStatus = rtl_math_ConversionStatus_OutOfRange; } + // else it's subnormal: allow it } p = bufmap[pCharParseEnd - buf]; bSign = false;
