Fix FloatToString printing with not enough precision when using `fixed` floatfield format
Setting precision to `std::numeric_limits<float>::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 <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
diff --git a/src/writer/float_to_string.cc b/src/writer/float_to_string.cc
index 81e66bc..900599d 100644
--- a/src/writer/float_to_string.cc
+++ b/src/writer/float_to_string.cc
@@ -19,6 +19,7 @@
#include <iomanip>
#include <limits>
#include <sstream>
+#include <functional>
#include "src/debug.h"
@@ -26,16 +27,31 @@
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<float>::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<float>();
+ 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<float>::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 4a0f64c..2ff278e 100644
--- a/src/writer/float_to_string_test.cc
+++ b/src/writer/float_to_string_test.cc
@@ -88,6 +88,13 @@
"-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