From 0b7f6b1864571ae8c6d3e227024d46c268ccd130 Mon Sep 17 00:00:00 2001 From: Shane Carr Date: Fri, 14 Feb 2020 18:24:14 -0800 Subject: [PATCH] ICU-20974 Correctly handle extreme values of double. --- icu4c/source/i18n/number_decimalquantity.cpp | 11 +++++---- icu4c/source/test/intltest/numbertest_api.cpp | 24 +++++++++++++++++++ .../number/DecimalQuantity_AbstractBCD.java | 8 ++++++- .../test/number/NumberFormatterApiTest.java | 18 ++++++++++++++ 4 files changed, 56 insertions(+), 5 deletions(-) diff --git a/icu4c/source/i18n/number_decimalquantity.cpp b/icu4c/source/i18n/number_decimalquantity.cpp index 2c584a40f25..482e93dc7a1 100644 --- a/icu4c/source/i18n/number_decimalquantity.cpp +++ b/icu4c/source/i18n/number_decimalquantity.cpp @@ -439,9 +439,6 @@ void DecimalQuantity::_setToDoubleFast(double n) { // TODO: Make a fast path for other types of doubles. if (!std::numeric_limits::is_iec559) { convertToAccurateDouble(); - // Turn off the approximate double flag, since the value is now exact. - isApproximate = false; - origDouble = 0.0; return; } @@ -456,8 +453,14 @@ void DecimalQuantity::_setToDoubleFast(double n) { return; } + if (exponent == -1023 || exponent == 1024) { + // The extreme values of exponent are special; use slow path. + convertToAccurateDouble(); + return; + } + // 3.3219... is log2(10) - auto fracLength = static_cast ((52 - exponent) / 3.32192809489); + auto fracLength = static_cast ((52 - exponent) / 3.32192809488736234787031942948939017586); if (fracLength >= 0) { int32_t i = fracLength; // 1e22 is the largest exact double. diff --git a/icu4c/source/test/intltest/numbertest_api.cpp b/icu4c/source/test/intltest/numbertest_api.cpp index 98c8ceb46e0..01494ff8ab9 100644 --- a/icu4c/source/test/intltest/numbertest_api.cpp +++ b/icu4c/source/test/intltest/numbertest_api.cpp @@ -1391,6 +1391,30 @@ void NumberFormatterApiTest::roundingOther() { u"1", u"1", u"0"); + + assertFormatSingle( + u"ICU-20974 Double.MIN_NORMAL", + u"scientific", + u"E0", + NumberFormatter::with().notation(Notation::scientific()), + Locale::getEnglish(), + DBL_MIN, + u"2.225074E-308"); + +#ifndef DBL_TRUE_MIN +#define DBL_TRUE_MIN 4.9E-324 +#endif + + // Note: this behavior is intentionally different from Java; see + // https://github.com/google/double-conversion/issues/126 + assertFormatSingle( + u"ICU-20974 Double.MIN_VALUE", + u"scientific", + u"E0", + NumberFormatter::with().notation(Notation::scientific()), + Locale::getEnglish(), + DBL_TRUE_MIN, + u"5E-324"); } void NumberFormatterApiTest::grouping() { diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/DecimalQuantity_AbstractBCD.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/DecimalQuantity_AbstractBCD.java index 5873bab9e3c..b1ead768c7c 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/DecimalQuantity_AbstractBCD.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/DecimalQuantity_AbstractBCD.java @@ -488,8 +488,14 @@ public abstract class DecimalQuantity_AbstractBCD implements DecimalQuantity { return; } + if (exponent == -1023 || exponent == 1024) { + // The extreme values of exponent are special; use slow path. + convertToAccurateDouble(); + return; + } + // 3.3219... is log2(10) - int fracLength = (int) ((52 - exponent) / 3.32192809489); + int fracLength = (int) ((52 - exponent) / 3.32192809488736234787031942948939017586); if (fracLength >= 0) { int i = fracLength; // 1e22 is the largest exact double. diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberFormatterApiTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberFormatterApiTest.java index 4188068b847..2512b1fc7aa 100644 --- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberFormatterApiTest.java +++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberFormatterApiTest.java @@ -1350,6 +1350,24 @@ public class NumberFormatterApiTest { "1", "1", "0"); + + assertFormatSingle( + "ICU-20974 Double.MIN_NORMAL", + "scientific", + "E0", + NumberFormatter.with().notation(Notation.scientific()), + ULocale.ENGLISH, + Double.MIN_NORMAL, + "2.225074E-308"); + + assertFormatSingle( + "ICU-20974 Double.MIN_VALUE", + "scientific", + "E0", + NumberFormatter.with().notation(Notation.scientific()), + ULocale.ENGLISH, + Double.MIN_VALUE, + "4.9E-324"); } @Test -- 2.40.0