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>
This commit is contained in:
Antonio Maiorano 2021-07-16 08:55:14 +00:00 committed by Tint LUCI CQ
parent 1da4073870
commit 4c9b440515
2 changed files with 32 additions and 9 deletions

View File

@ -19,6 +19,7 @@
#include <iomanip> #include <iomanip>
#include <limits> #include <limits>
#include <sstream> #include <sstream>
#include <functional>
#include "src/debug.h" #include "src/debug.h"
@ -26,16 +27,31 @@ namespace tint {
namespace writer { namespace writer {
std::string FloatToString(float f) { std::string FloatToString(float f) {
std::stringstream ss; // Try printing the float in fixed point, with a smallish limit on the
ss.flags(ss.flags() | std::ios_base::showpoint | std::ios_base::fixed); // precision
ss.precision(std::numeric_limits<float>::max_digits10); std::stringstream fixed;
ss << f; fixed.flags(fixed.flags() | std::ios_base::showpoint | std::ios_base::fixed);
auto str = ss.str(); fixed.precision(9);
while (str.length() >= 2 && str[str.size() - 1] == '0' && fixed << f;
str[str.size() - 2] != '.') {
str.pop_back(); // 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) { std::string FloatToBitPreservingString(float f) {

View File

@ -88,6 +88,13 @@ TEST(FloatToStringTest, Lowest) {
"-340282346638528859811704183484516925440.0"); "-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 // FloatToBitPreservingString
// //
// First replicate the tests for FloatToString // First replicate the tests for FloatToString