Improve robustness of FloatToString.

When converting floats to string, we now enforce the "C" locale, so
decimal points will be written as "." rather than the "," separator
used natively in some European locales.

Also, we now use operator>> to read back the number instead of
std::stof. std::stof works in the system locale, and will fail to
read back floats with the wrong decimal separator. (Also, std::stof
will throw if the number is out of range and can't fit in the
destination, which implies that the `if` check was probably never
failing.)

Skia encountered similar issues: see http://review.skia.org/587536
for the Skia implementation.

Change-Id: I5aded6acc7cfcf2ad4d5b974bc30c3b645eaec51
Bug: dawn:1686
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/104680
Reviewed-by: Dan Sinclair <dsinclair@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: John Stiles <johnstiles@google.com>
This commit is contained in:
John Stiles 2022-10-05 15:47:25 +00:00 committed by Dawn LUCI CQ
parent 8a833840d1
commit 761536d941
1 changed files with 10 additions and 3 deletions

View File

@ -30,13 +30,19 @@ std::string FloatToString(float f) {
// precision
std::stringstream fixed;
fixed.flags(fixed.flags() | std::ios_base::showpoint | std::ios_base::fixed);
fixed.imbue(std::locale::classic());
fixed.precision(9);
fixed << f;
std::string str = fixed.str();
// If this string can be parsed without loss of information, use it.
// (Use double here to dodge a bug in older libc++ versions which
// would incorrectly read back FLT_MAX as INF.)
double roundtripped;
fixed >> roundtripped;
// 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();
if (float_equal_no_warning(f, static_cast<float>(roundtripped))) {
while (str.length() >= 2 && str[str.size() - 1] == '0' && str[str.size() - 2] != '.') {
str.pop_back();
}
@ -47,6 +53,7 @@ std::string FloatToString(float f) {
// Resort to scientific, with the minimum precision needed to preserve the
// whole float
std::stringstream sci;
sci.imbue(std::locale::classic());
sci.precision(std::numeric_limits<float>::max_digits10);
sci << f;
return sci.str();