Fix ostream << floating_point not correctly handling precision (#1173)

1. Hexfloat output now ignores precision as required by the standard. (Fixes #1125.)
2. Zero precision is now correctly passed to sprintf.
3. Negative precision no longer crashes.
This commit is contained in:
statementreply 2020-08-22 10:20:19 +08:00 коммит произвёл GitHub
Родитель 465d0a5e13
Коммит 0b81475cc8
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
3 изменённых файлов: 257 добавлений и 10 удалений

Просмотреть файл

@ -1146,6 +1146,45 @@ __PURE_APPDOMAIN_GLOBAL locale::id num_get<_Elem, _InIt>::id;
#pragma clang diagnostic pop #pragma clang diagnostic pop
#endif // __clang__ #endif // __clang__
// STRUCT TEMPLATE _Hex_float_precision
template <class _Ty>
struct _Hex_float_precision;
template <>
struct _Hex_float_precision<double> {
// 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<long double> {
// 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 <class _Ty>
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<int>(_Precision);
} else if (_Precision == 0) {
const bool _Is_default_float = _Float_flags == 0;
if (_Is_default_float) {
return 1;
} else {
return 0;
}
} else {
constexpr int _Default_precision = 6;
return _Default_precision;
}
}
// CLASS TEMPLATE num_put // CLASS TEMPLATE num_put
template <class _Elem, class _OutIt = ostreambuf_iterator<_Elem, char_traits<_Elem>>> template <class _Elem, class _OutIt = ostreambuf_iterator<_Elem, char_traits<_Elem>>>
class num_put : public locale::facet { // facet for converting encoded numbers to text 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 _OutIt _Dest, ios_base& _Iosbase, _Elem _Fill, double _Val) const { // put formatted double to _Dest
string _Buf; string _Buf;
char _Fmt[8]; char _Fmt[8];
bool _Isfixed = (_Iosbase.flags() & ios_base::floatfield) == ios_base::fixed; const auto _Float_flags = _Iosbase.flags() & ios_base::floatfield;
streamsize _Precision = _Iosbase.precision() <= 0 && !_Isfixed ? 6 : _Iosbase.precision(); // desired precision const bool _Is_fixed = _Float_flags == ios_base::fixed;
size_t _Bufsize = static_cast<size_t>(_Precision); const bool _Is_hex = _Float_flags == (ios_base::fixed | ios_base::scientific);
if (_Isfixed && 1e10 < _CSTD fabs(_Val)) { // f or F format const streamsize _Precision = _Is_hex ? -1 : _Iosbase.precision(); // precision setting
const int _Desired_precision =
_Float_put_desired_precision<double>(_Precision, _Float_flags); // desired precision
size_t _Bufsize = static_cast<size_t>(_Desired_precision);
if (_Is_fixed && 1e10 < _CSTD fabs(_Val)) { // f or F format
int _Ptwo; int _Ptwo;
(void) _CSTD frexp(_Val, &_Ptwo); (void) _CSTD frexp(_Val, &_Ptwo);
_Bufsize += _CSTD abs(_Ptwo) * 30103L / 100000L; _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 _OutIt _Dest, ios_base& _Iosbase, _Elem _Fill, long double _Val) const { // put formatted long double to _Dest
string _Buf; string _Buf;
char _Fmt[8]; char _Fmt[8];
bool _Isfixed = (_Iosbase.flags() & ios_base::floatfield) == ios_base::fixed; const auto _Float_flags = _Iosbase.flags() & ios_base::floatfield;
streamsize _Precision = _Iosbase.precision() <= 0 && !_Isfixed ? 6 : _Iosbase.precision(); // desired precision const bool _Is_fixed = _Float_flags == ios_base::fixed;
size_t _Bufsize = static_cast<size_t>(_Precision); const bool _Is_hex = _Float_flags == (ios_base::fixed | ios_base::scientific);
if (_Isfixed && 1e10 < _CSTD fabsl(_Val)) { // f or F format const streamsize _Precision = _Is_hex ? -1 : _Iosbase.precision(); // precision setting
const int _Desired_precision =
_Float_put_desired_precision<long double>(_Precision, _Float_flags); // desired precision
size_t _Bufsize = static_cast<size_t>(_Desired_precision);
if (_Is_fixed && 1e10 < _CSTD fabsl(_Val)) { // f or F format
int _Ptwo; int _Ptwo;
(void) _CSTD frexpl(_Val, &_Ptwo); (void) _CSTD frexpl(_Val, &_Ptwo);
_Bufsize += _CSTD abs(_Ptwo) * 30103L / 100000L; _Bufsize += _CSTD abs(_Ptwo) * 30103L / 100000L;

Просмотреть файл

@ -213,9 +213,109 @@ void test_main() { // test basic workings of ostream definitions
outs << STD hexfloat << 2.0; outs << STD hexfloat << 2.0;
STD string ans = outs.str(); STD string ans = outs.str();
const char* buf = ans.c_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");
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 // test Boolx inserter
const Boolx no(0), yes(1); const Boolx no(0), yes(1);
outs.str(""); outs.str("");

Просмотреть файл

@ -208,7 +208,107 @@ void test_main() { // test basic workings of ostream definitions
outs << STD hexfloat << 2.0; outs << STD hexfloat << 2.0;
STD wstring ans = outs.str(); STD wstring ans = outs.str();
const wchar_t* buf = ans.c_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");
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 // test Boolx inserter
const Boolx no(0), yes(1); const Boolx no(0), yes(1);