From b9e6aa91d187a733ed229525366e14fdb9f27701 Mon Sep 17 00:00:00 2001 From: statementreply Date: Wed, 19 Aug 2020 00:17:46 +0800 Subject: [PATCH 1/5] Fix ostream << floating_point not correctly handling precision 1. Hexfloat output ignores precision now as required by the standard. 2. Precision of zero is now correctly passed to sprintf. 3. Fixed output with negative precision no longer crashes. --- stl/inc/xlocnum | 63 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 55 insertions(+), 8 deletions(-) diff --git a/stl/inc/xlocnum b/stl/inc/xlocnum index e2c887b5f1f..a56d5c1e28e 100644 --- a/stl/inc/xlocnum +++ b/stl/inc/xlocnum @@ -1146,6 +1146,45 @@ __PURE_APPDOMAIN_GLOBAL locale::id num_get<_Elem, _InIt>::id; #pragma clang diagnostic pop #endif // __clang__ +// STRUCT TEMPLATE _Hex_float_precision +template +struct _Hex_float_precision; + +template <> +struct _Hex_float_precision { + // the number of hexits needed to represent (DBL_MANT_DIG - 1) bits after the radix point exactly + static constexpr int value = ((DBL_MANT_DIG - 1) + 3) / 4; +}; + +template <> +struct _Hex_float_precision { + // the number of hexits needed to represent (LDBL_MANT_DIG - 1) bits after the radix point exactly + static constexpr int value = ((LDBL_MANT_DIG - 1) + 3) / 4; +}; + +// FUNCTION TEMPLATE _Float_put_desired_precision +template +int _Float_put_desired_precision(const streamsize _Precision, const ios_base::fmtflags _Float_flags) { + if (_Precision > 0) { + return static_cast(_Precision); + } else if (_Precision == 0) { + const bool _Is_default_float = _Float_flags == 0; + if (_Is_default_float) { + return 1; + } else { + return 0; + } + } else { + const bool _Is_hex = _Float_flags == (ios_base::fixed | ios_base::scientific); + if (_Is_hex) { + return _Hex_float_precision<_Ty>::value; + } else { + constexpr int _Default_precision = 6; + return _Default_precision; + } + } +} + // CLASS TEMPLATE num_put template >> class num_put : public locale::facet { // facet for converting encoded numbers to text @@ -1292,10 +1331,14 @@ protected: _OutIt _Dest, ios_base& _Iosbase, _Elem _Fill, double _Val) const { // put formatted double to _Dest string _Buf; char _Fmt[8]; - bool _Isfixed = (_Iosbase.flags() & ios_base::floatfield) == ios_base::fixed; - streamsize _Precision = _Iosbase.precision() <= 0 && !_Isfixed ? 6 : _Iosbase.precision(); // desired precision - size_t _Bufsize = static_cast(_Precision); - if (_Isfixed && 1e10 < _CSTD fabs(_Val)) { // f or F format + const auto _Float_flags = _Iosbase.flags() & ios_base::floatfield; + const bool _Is_fixed = _Float_flags == ios_base::fixed; + const bool _Is_hex = _Float_flags == (ios_base::fixed | ios_base::scientific); + const streamsize _Precision = _Is_hex ? -1 : _Iosbase.precision(); // precision setting + const int _Desired_precision = + _Float_put_desired_precision(_Precision, _Float_flags); // desired precision + size_t _Bufsize = static_cast(_Desired_precision); + if (_Is_fixed && 1e10 < _CSTD fabs(_Val)) { // f or F format int _Ptwo; (void) _CSTD frexp(_Val, &_Ptwo); _Bufsize += _CSTD abs(_Ptwo) * 30103L / 100000L; @@ -1312,10 +1355,14 @@ protected: _OutIt _Dest, ios_base& _Iosbase, _Elem _Fill, long double _Val) const { // put formatted long double to _Dest string _Buf; char _Fmt[8]; - bool _Isfixed = (_Iosbase.flags() & ios_base::floatfield) == ios_base::fixed; - streamsize _Precision = _Iosbase.precision() <= 0 && !_Isfixed ? 6 : _Iosbase.precision(); // desired precision - size_t _Bufsize = static_cast(_Precision); - if (_Isfixed && 1e10 < _CSTD fabsl(_Val)) { // f or F format + const auto _Float_flags = _Iosbase.flags() & ios_base::floatfield; + const bool _Is_fixed = _Float_flags == ios_base::fixed; + const bool _Is_hex = _Float_flags == (ios_base::fixed | ios_base::scientific); + const streamsize _Precision = _Is_hex ? -1 : _Iosbase.precision(); // precision setting + const int _Desired_precision = + _Float_put_desired_precision(_Precision, _Float_flags); // desired precision + size_t _Bufsize = static_cast(_Desired_precision); + if (_Is_fixed && 1e10 < _CSTD fabsl(_Val)) { // f or F format int _Ptwo; (void) _CSTD frexpl(_Val, &_Ptwo); _Bufsize += _CSTD abs(_Ptwo) * 30103L / 100000L; From 98f8e7a7b8b68d0ba54b687eb0b39b75deee698e Mon Sep 17 00:00:00 2001 From: statementreply Date: Wed, 19 Aug 2020 00:38:47 +0800 Subject: [PATCH 2/5] Add test cases --- tests/tr1/tests/ostream1/test.cpp | 43 ++++++++++++++++++++++++++++++- tests/tr1/tests/ostream2/test.cpp | 43 ++++++++++++++++++++++++++++++- 2 files changed, 84 insertions(+), 2 deletions(-) diff --git a/tests/tr1/tests/ostream1/test.cpp b/tests/tr1/tests/ostream1/test.cpp index f19c5727112..64bc931e535 100644 --- a/tests/tr1/tests/ostream1/test.cpp +++ b/tests/tr1/tests/ostream1/test.cpp @@ -213,9 +213,50 @@ void test_main() { // test basic workings of ostream definitions outs << STD hexfloat << 2.0; STD string ans = outs.str(); const char* buf = ans.c_str(); - CHECK_STR(buf, "0x1.000p+1"); + CHECK_STR(buf, "0x1.0000000000000p+1"); } + outs.precision(0); + + outs.str(""); + outs << STD defaultfloat << 1.5; + CHECK_STR(outs.str().c_str(), "2"); + + outs.str(""); + outs << STD fixed << 1.0; + CHECK_STR(outs.str().c_str(), "1"); + + outs.str(""); + outs << STD scientific << 2.0; + CHECK_STR(outs.str().c_str(), "2e+00"); + + outs.str(""); + outs << STD hexfloat << 2.0; + CHECK_STR(outs.str().c_str(), "0x1.0000000000000p+1"); + + outs.precision(-1); + + outs.str(""); + outs << STD defaultfloat << 1.5; + CHECK_STR(outs.str().c_str(), "1.5"); + + outs.str(""); + outs << STD fixed << 1.0; + CHECK_STR(outs.str().c_str(), "1.000000"); + + outs.str(""); + outs << STD scientific << 2.0; + CHECK_STR(outs.str().c_str(), "2.000000e+00"); + + outs.str(""); + outs << STD hexfloat << 2.0; + CHECK_STR(outs.str().c_str(), "0x1.0000000000000p+1"); + + outs.precision(-49); + outs.str(""); + outs << STD fixed << 1.0; + CHECK_STR(outs.str().c_str(), "1.000000"); + // test Boolx inserter const Boolx no(0), yes(1); outs.str(""); diff --git a/tests/tr1/tests/ostream2/test.cpp b/tests/tr1/tests/ostream2/test.cpp index 4fe35d436db..b042590466f 100644 --- a/tests/tr1/tests/ostream2/test.cpp +++ b/tests/tr1/tests/ostream2/test.cpp @@ -208,7 +208,48 @@ void test_main() { // test basic workings of ostream definitions outs << STD hexfloat << 2.0; STD wstring ans = outs.str(); const wchar_t* buf = ans.c_str(); - CHECK_WSTR(buf, L"0x1.000p+1"); + CHECK_WSTR(buf, L"0x1.0000000000000p+1"); + + outs.precision(0); + + outs.str(L""); + outs << STD defaultfloat << 1.5; + CHECK_WSTR(outs.str().c_str(), L"2"); + + outs.str(L""); + outs << STD fixed << 1.0; + CHECK_WSTR(outs.str().c_str(), L"1"); + + outs.str(L""); + outs << STD scientific << 2.0; + CHECK_WSTR(outs.str().c_str(), L"2e+00"); + + outs.str(L""); + outs << STD hexfloat << 2.0; + CHECK_WSTR(outs.str().c_str(), L"0x1.0000000000000p+1"); + + outs.precision(-1); + + outs.str(L""); + outs << STD defaultfloat << 1.5; + CHECK_WSTR(outs.str().c_str(), L"1.5"); + + outs.str(L""); + outs << STD fixed << 1.0; + CHECK_WSTR(outs.str().c_str(), L"1.000000"); + + outs.str(L""); + outs << STD scientific << 2.0; + CHECK_WSTR(outs.str().c_str(), L"2.000000e+00"); + + outs.str(L""); + outs << STD hexfloat << 2.0; + CHECK_WSTR(outs.str().c_str(), L"0x1.0000000000000p+1"); + + outs.precision(-49); + outs.str(L""); + outs << STD fixed << 1.0; + CHECK_WSTR(outs.str().c_str(), L"1.000000"); // test Boolx inserter const Boolx no(0), yes(1); From cd8d15cfe778bc0286eb8aa68218c97297192bf5 Mon Sep 17 00:00:00 2001 From: statementreply Date: Wed, 19 Aug 2020 00:56:37 +0800 Subject: [PATCH 3/5] Make _Float_put_desired_precision work for both raw precision() and _Precision in do_put --- stl/inc/xlocnum | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/stl/inc/xlocnum b/stl/inc/xlocnum index a56d5c1e28e..1da59e2b63b 100644 --- a/stl/inc/xlocnum +++ b/stl/inc/xlocnum @@ -1165,6 +1165,11 @@ struct _Hex_float_precision { // FUNCTION TEMPLATE _Float_put_desired_precision template int _Float_put_desired_precision(const streamsize _Precision, const ios_base::fmtflags _Float_flags) { + const bool _Is_hex = _Float_flags == (ios_base::fixed | ios_base::scientific); + if (_Is_hex) { + return _Hex_float_precision<_Ty>::value; + } + if (_Precision > 0) { return static_cast(_Precision); } else if (_Precision == 0) { @@ -1175,13 +1180,8 @@ int _Float_put_desired_precision(const streamsize _Precision, const ios_base::fm return 0; } } else { - const bool _Is_hex = _Float_flags == (ios_base::fixed | ios_base::scientific); - if (_Is_hex) { - return _Hex_float_precision<_Ty>::value; - } else { - constexpr int _Default_precision = 6; - return _Default_precision; - } + constexpr int _Default_precision = 6; + return _Default_precision; } } From eda71ae0659a25e9a2fbc31499113828a6ea765d Mon Sep 17 00:00:00 2001 From: statementreply Date: Wed, 19 Aug 2020 01:14:34 +0800 Subject: [PATCH 4/5] Add test coverage for long double --- tests/tr1/tests/ostream1/test.cpp | 59 +++++++++++++++++++++++++++++++ tests/tr1/tests/ostream2/test.cpp | 59 +++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+) diff --git a/tests/tr1/tests/ostream1/test.cpp b/tests/tr1/tests/ostream1/test.cpp index 64bc931e535..74bc1791572 100644 --- a/tests/tr1/tests/ostream1/test.cpp +++ b/tests/tr1/tests/ostream1/test.cpp @@ -257,6 +257,65 @@ void test_main() { // test basic workings of ostream definitions outs << STD fixed << 1.0; CHECK_STR(outs.str().c_str(), "1.000000"); + outs.precision(3); + + outs.str(""); + outs << STD defaultfloat << 1.5L; + CHECK_STR(outs.str().c_str(), "1.5"); + + outs.str(""); + outs << STD fixed << 1.0L; + CHECK_STR(outs.str().c_str(), "1.000"); + + outs.str(""); + outs << STD scientific << 2.0L; + CHECK_STR(outs.str().c_str(), "2.000e+00"); + + outs.str(""); + outs << STD hexfloat << 2.0L; + CHECK_STR(outs.str().c_str(), "0x1.0000000000000p+1"); + + outs.precision(0); + + outs.str(""); + outs << STD defaultfloat << 1.5L; + CHECK_STR(outs.str().c_str(), "2"); + + outs.str(""); + outs << STD fixed << 1.0L; + CHECK_STR(outs.str().c_str(), "1"); + + outs.str(""); + outs << STD scientific << 2.0L; + CHECK_STR(outs.str().c_str(), "2e+00"); + + outs.str(""); + outs << STD hexfloat << 2.0L; + CHECK_STR(outs.str().c_str(), "0x1.0000000000000p+1"); + + outs.precision(-1); + + outs.str(""); + outs << STD defaultfloat << 1.5L; + CHECK_STR(outs.str().c_str(), "1.5"); + + outs.str(""); + outs << STD fixed << 1.0L; + CHECK_STR(outs.str().c_str(), "1.000000"); + + outs.str(""); + outs << STD scientific << 2.0L; + CHECK_STR(outs.str().c_str(), "2.000000e+00"); + + outs.str(""); + outs << STD hexfloat << 2.0L; + CHECK_STR(outs.str().c_str(), "0x1.0000000000000p+1"); + + outs.precision(-49); + outs.str(""); + outs << STD fixed << 1.0L; + CHECK_STR(outs.str().c_str(), "1.000000"); + // test Boolx inserter const Boolx no(0), yes(1); outs.str(""); diff --git a/tests/tr1/tests/ostream2/test.cpp b/tests/tr1/tests/ostream2/test.cpp index b042590466f..6a2701ecb91 100644 --- a/tests/tr1/tests/ostream2/test.cpp +++ b/tests/tr1/tests/ostream2/test.cpp @@ -251,6 +251,65 @@ void test_main() { // test basic workings of ostream definitions outs << STD fixed << 1.0; CHECK_WSTR(outs.str().c_str(), L"1.000000"); + outs.precision(3); + + outs.str(L""); + outs << STD defaultfloat << 1.5L; + CHECK_WSTR(outs.str().c_str(), L"1.5"); + + outs.str(L""); + outs << STD fixed << 1.0L; + CHECK_WSTR(outs.str().c_str(), L"1.000"); + + outs.str(L""); + outs << STD scientific << 2.0L; + CHECK_WSTR(outs.str().c_str(), L"2.000e+00"); + + outs.str(L""); + outs << STD hexfloat << 2.0L; + CHECK_WSTR(outs.str().c_str(), L"0x1.0000000000000p+1"); + + outs.precision(0); + + outs.str(L""); + outs << STD defaultfloat << 1.5L; + CHECK_WSTR(outs.str().c_str(), L"2"); + + outs.str(L""); + outs << STD fixed << 1.0L; + CHECK_WSTR(outs.str().c_str(), L"1"); + + outs.str(L""); + outs << STD scientific << 2.0L; + CHECK_WSTR(outs.str().c_str(), L"2e+00"); + + outs.str(L""); + outs << STD hexfloat << 2.0L; + CHECK_WSTR(outs.str().c_str(), L"0x1.0000000000000p+1"); + + outs.precision(-1); + + outs.str(L""); + outs << STD defaultfloat << 1.5L; + CHECK_WSTR(outs.str().c_str(), L"1.5"); + + outs.str(L""); + outs << STD fixed << 1.0L; + CHECK_WSTR(outs.str().c_str(), L"1.000000"); + + outs.str(L""); + outs << STD scientific << 2.0L; + CHECK_WSTR(outs.str().c_str(), L"2.000000e+00"); + + outs.str(L""); + outs << STD hexfloat << 2.0L; + CHECK_WSTR(outs.str().c_str(), L"0x1.0000000000000p+1"); + + outs.precision(-49); + outs.str(L""); + outs << STD fixed << 1.0L; + CHECK_WSTR(outs.str().c_str(), L"1.000000"); + // test Boolx inserter const Boolx no(0), yes(1); outs.str(L""); From ae328d726554e21784e15b25f6cb09e361f684e1 Mon Sep 17 00:00:00 2001 From: statementreply Date: Wed, 19 Aug 2020 14:08:07 +0800 Subject: [PATCH 5/5] Fix copy paste error --- stl/inc/xlocnum | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/xlocnum b/stl/inc/xlocnum index 1da59e2b63b..0c0bd1db376 100644 --- a/stl/inc/xlocnum +++ b/stl/inc/xlocnum @@ -1336,7 +1336,7 @@ protected: const bool _Is_hex = _Float_flags == (ios_base::fixed | ios_base::scientific); const streamsize _Precision = _Is_hex ? -1 : _Iosbase.precision(); // precision setting const int _Desired_precision = - _Float_put_desired_precision(_Precision, _Float_flags); // desired precision + _Float_put_desired_precision(_Precision, _Float_flags); // desired precision size_t _Bufsize = static_cast(_Desired_precision); if (_Is_fixed && 1e10 < _CSTD fabs(_Val)) { // f or F format int _Ptwo;