Fix negative zero output when formatting flaoting point values close to zero.

This commit is contained in:
wima (Mathias Winterhalter) 2022-03-30 16:24:08 +02:00
parent 288c3b928b
commit 60fd493627
3 changed files with 55 additions and 0 deletions

View File

@ -2554,11 +2554,30 @@ template <typename Char> struct arg_formatter {
FMT_CONSTEXPR FMT_INLINE auto operator()(T value) -> iterator {
return detail::write(out, value, specs, locale);
}
FMT_CONSTEXPR FMT_INLINE auto operator()(float value) -> iterator {
return detail::write(out, prevent_negative_zero(value), specs, locale);
}
FMT_CONSTEXPR FMT_INLINE auto operator()(double value) -> iterator {
return detail::write(out, prevent_negative_zero(value), specs, locale);
}
FMT_CONSTEXPR FMT_INLINE auto operator()(long double value) -> iterator {
return detail::write(out, prevent_negative_zero(value), specs, locale);
}
auto operator()(typename basic_format_arg<context>::handle) -> iterator {
// User-defined types are handled separately because they require access
// to the parse context.
return out;
}
private:
template <typename T>
FMT_CONSTEXPR FMT_INLINE auto prevent_negative_zero(T value) -> T {
if (this->specs.precision > 0 and
(T{1} / static_cast<T>(std::pow(10, this->specs.precision)) > std::fabs(value))) {
value = 0;
}
return value;
}
};
template <typename Char> struct custom_formatter {

View File

@ -267,6 +267,10 @@ class printf_arg_formatter : public arg_formatter<Char> {
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
OutputIt operator()(T value) {
if (this->specs.precision > 0 and
(T{1} / static_cast<T>(std::pow(10, this->specs.precision)) > std::fabs(value))) {
value = 0;
}
return base::operator()(value);
}

View File

@ -581,3 +581,35 @@ TEST(printf_test, vsprintf_make_wargs_example) {
fmt::vsprintf(L"[%d] %s happened",
{fmt::make_wprintf_args(42, L"something")}));
}
TEST(printf_test, check_sign_truncation_on_zero) {
float fval = 0.0f - 1e-7f;
EXPECT_EQ(fmt::vsprintf("%1.7f", {fmt::make_printf_args(fval)}), "-0.0000001");
EXPECT_EQ(fmt::format("{:1.7f}", fval), "-0.0000001");
fval = std::nexttoward(fval, 1.f);
EXPECT_EQ(fmt::vsprintf("%1.7f", {fmt::make_printf_args(fval)}), "0.0000000");
EXPECT_EQ(fmt::format("{:1.7f}", fval), "0.0000000");
fval = std::nexttoward(fval, -1.f);
EXPECT_EQ(fmt::vsprintf("%1.7f", {fmt::make_printf_args(fval)}), "-0.0000001");
EXPECT_EQ(fmt::format("{:1.7f}", fval), "-0.0000001");
double dval = 0.0 - 1e-15;
EXPECT_EQ(fmt::vsprintf("%1.15lf", {fmt::make_printf_args(dval)}), "-0.000000000000001");
EXPECT_EQ(fmt::format("{:1.15f}", dval), "-0.000000000000001");
dval = std::nexttoward(dval, 1.f);
EXPECT_EQ(fmt::vsprintf("%1.15lf", {fmt::make_printf_args(dval)}), "0.000000000000000");
EXPECT_EQ(fmt::format("{:1.15f}", dval), "0.000000000000000");
dval = std::nexttoward(dval, -1.f);
EXPECT_EQ(fmt::vsprintf("%1.15lf", {fmt::make_printf_args(dval)}), "-0.000000000000001");
EXPECT_EQ(fmt::format("{:1.15f}", dval), "-0.000000000000001");
long double ldval = 0.0L - 1e-18L;
EXPECT_EQ(fmt::vsprintf("%1.18Lf", {fmt::make_printf_args(ldval)}), "-0.000000000000000001");
EXPECT_EQ(fmt::format("{:1.18f}", ldval), "-0.000000000000000001");
ldval = std::nexttoward(ldval, 1.f);
EXPECT_EQ(fmt::vsprintf("%1.18Lf", {fmt::make_printf_args(ldval)}), "0.000000000000000000");
EXPECT_EQ(fmt::format("{:1.18f}", ldval), "0.000000000000000000");
ldval = std::nexttoward(ldval, -1.f);
EXPECT_EQ(fmt::vsprintf("%1.18Lf", {fmt::make_printf_args(ldval)}), "-0.000000000000000001");
EXPECT_EQ(fmt::format("{:1.18f}", ldval), "-0.000000000000000001");
}