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:
parent
1da4073870
commit
4c9b440515
|
@ -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,18 +27,33 @@ 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);
|
||||||
|
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' &&
|
while (str.length() >= 2 && str[str.size() - 1] == '0' &&
|
||||||
str[str.size() - 2] != '.') {
|
str[str.size() - 2] != '.') {
|
||||||
str.pop_back();
|
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) {
|
||||||
// For the NaN case, avoid handling the number as a floating point value.
|
// For the NaN case, avoid handling the number as a floating point value.
|
||||||
// Some machines will modify the top bit in the mantissa of a NaN.
|
// Some machines will modify the top bit in the mantissa of a NaN.
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue