From 4c9b4405153bc1ea98efc9105002d1a4d24c6d71 Mon Sep 17 00:00:00 2001 From: Antonio Maiorano Date: Fri, 16 Jul 2021 08:55:14 +0000 Subject: [PATCH] Fix FloatToString printing with not enough precision when using `fixed` floatfield format Setting precision to `std::numeric_limits::max_digits10` is valid when using the `scientific` floatfield format when printing values. However, we used `fixed` to make our floats more human-readable. This change keeps the output in `fixed`, except if doing so loses precision, in which case we fall back to `scientific`. This fixes the rendering differences seen in the Babylon.js examples (https://crbug.com/tint/944) between Dawn using Tint vs SPIRV-Cross, as Tint's output was emitting values that had lost too much precision (e.g. very small numbers being output as 0). Bug: tint:944 Change-Id: I8deea23ad876825bbe390fc26907d4bbbd4b966e Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/58321 Commit-Queue: Ben Clayton Kokoro: Kokoro Reviewed-by: Ben Clayton --- src/writer/float_to_string.cc | 34 ++++++++++++++++++++++-------- src/writer/float_to_string_test.cc | 7 ++++++ 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/src/writer/float_to_string.cc b/src/writer/float_to_string.cc index 81e66bc1b8..900599dd09 100644 --- a/src/writer/float_to_string.cc +++ b/src/writer/float_to_string.cc @@ -19,6 +19,7 @@ #include #include #include +#include #include "src/debug.h" @@ -26,16 +27,31 @@ namespace tint { namespace writer { std::string FloatToString(float f) { - std::stringstream ss; - ss.flags(ss.flags() | std::ios_base::showpoint | std::ios_base::fixed); - ss.precision(std::numeric_limits::max_digits10); - ss << f; - auto str = ss.str(); - while (str.length() >= 2 && str[str.size() - 1] == '0' && - str[str.size() - 2] != '.') { - str.pop_back(); + // Try printing the float in fixed point, with a smallish limit on the + // precision + std::stringstream fixed; + fixed.flags(fixed.flags() | std::ios_base::showpoint | std::ios_base::fixed); + fixed.precision(9); + fixed << f; + + // If this string can be parsed without loss of information, use it + auto float_equal_no_warning = std::equal_to(); + if (float_equal_no_warning(std::stof(fixed.str()), f)) { + auto str = fixed.str(); + while (str.length() >= 2 && str[str.size() - 1] == '0' && + str[str.size() - 2] != '.') { + str.pop_back(); + } + + return str; } - return str; + + // Resort to scientific, with the minimum precision needed to preserve the + // whole float + std::stringstream sci; + sci.precision(std::numeric_limits::max_digits10); + sci << f; + return sci.str(); } std::string FloatToBitPreservingString(float f) { diff --git a/src/writer/float_to_string_test.cc b/src/writer/float_to_string_test.cc index 4a0f64cea4..2ff278e2b8 100644 --- a/src/writer/float_to_string_test.cc +++ b/src/writer/float_to_string_test.cc @@ -88,6 +88,13 @@ TEST(FloatToStringTest, Lowest) { "-340282346638528859811704183484516925440.0"); } +TEST(FloatToStringTest, Precision) { + EXPECT_EQ(FloatToString(1e-8f), "0.00000001"); + EXPECT_EQ(FloatToString(1e-9f), "0.000000001"); + EXPECT_EQ(FloatToString(1e-10f), "1.00000001e-10"); + EXPECT_EQ(FloatToString(1e-20f), "9.99999968e-21"); +} + // FloatToBitPreservingString // // First replicate the tests for FloatToString