Support float values where the zeros change the exponent
When underflow is detected in float parsing, WGSL returns `0.0`.
Currently, if the number of `0`s following a `.` shifts the exponent
from a positive to a negative we were not correctly identify the
underflow and returning 0. This CL updates the float parsing to detect
undeflow in this case and return `0.0` as expected.
Bug: tint:1863
Change-Id: I164063cebf70f825fdf2753dff8a4f016939c38e
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/124341
Commit-Queue: David Neto <dneto@google.com>
Reviewed-by: David Neto <dneto@google.com>
Auto-Submit: Dan Sinclair <dsinclair@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/tint/reader/wgsl/lexer.cc b/src/tint/reader/wgsl/lexer.cc
index 91c2993..f795053 100644
--- a/src/tint/reader/wgsl/lexer.cc
+++ b/src/tint/reader/wgsl/lexer.cc
@@ -331,18 +331,32 @@
auto source = begin_source();
bool has_mantissa_digits = false;
+ std::optional<size_t> first_significant_digit_position;
while (end < length() && is_digit(at(end))) {
+ if (!first_significant_digit_position.has_value() && (at(end) != '0')) {
+ first_significant_digit_position = end;
+ }
+
has_mantissa_digits = true;
end++;
}
- bool has_point = false;
+ std::optional<size_t> dot_position;
if (end < length() && matches(end, '.')) {
- has_point = true;
+ dot_position = end;
end++;
}
+ size_t zeros_before_digit = 0;
while (end < length() && is_digit(at(end))) {
+ if (!first_significant_digit_position.has_value()) {
+ if (at(end) == '0') {
+ zeros_before_digit += 1;
+ } else {
+ first_significant_digit_position = end;
+ }
+ }
+
has_mantissa_digits = true;
end++;
}
@@ -352,7 +366,7 @@
}
// Parse the exponent if one exists
- bool has_exponent = false;
+ std::optional<size_t> exponent_value_position;
bool negative_exponent = false;
if (end < length() && (matches(end, 'e') || matches(end, 'E'))) {
end++;
@@ -360,14 +374,16 @@
negative_exponent = matches(end, '-');
end++;
}
+ exponent_value_position = end;
+ bool has_digits = false;
while (end < length() && isdigit(at(end))) {
- has_exponent = true;
+ has_digits = true;
end++;
}
// If an 'e' or 'E' was present, then the number part must also be present.
- if (!has_exponent) {
+ if (!has_digits) {
const auto str = std::string{substr(start, end - start)};
return {Token::Type::kError, source,
"incomplete exponent for floating point literal: " + str};
@@ -382,7 +398,8 @@
has_h_suffix = true;
}
- if (!has_point && !has_exponent && !has_f_suffix && !has_h_suffix) {
+ if (!dot_position.has_value() && !exponent_value_position.has_value() && !has_f_suffix &&
+ !has_h_suffix) {
// If it only has digits then it's an integer.
return {};
}
@@ -399,10 +416,32 @@
auto ret = absl::from_chars(&at(start), end_ptr, value);
bool overflow = ret.ec != std::errc();
- // The provided value did not fit in a double and has a negative exponent, so treat it as a 0.
- if (negative_exponent && ret.ec == std::errc::result_out_of_range) {
- overflow = false;
- value = 0.0;
+ // Value didn't fit in a double, check for underflow as that is 0.0 in WGSL and not an error.
+ if (ret.ec == std::errc::result_out_of_range) {
+ // The exponent is negative, so treat as underflow
+ if (negative_exponent) {
+ overflow = false;
+ value = 0.0;
+ } else if (dot_position.has_value() && first_significant_digit_position.has_value() &&
+ first_significant_digit_position.value() > dot_position.value()) {
+ // Parse the exponent from the float if provided
+ size_t exp_value = 0;
+ bool exp_conversion_succeeded = true;
+ if (exponent_value_position.has_value()) {
+ auto exp_end_ptr = end_ptr - (has_f_suffix || has_h_suffix ? 1 : 0);
+ auto exp_ret = std::from_chars(&at(exponent_value_position.value()), exp_end_ptr,
+ exp_value, 10);
+
+ if (exp_ret.ec != std::errc{}) {
+ exp_conversion_succeeded = false;
+ }
+ }
+ // If the exponent has gone negative, then this is an underflow case
+ if (exp_conversion_succeeded && exp_value < zeros_before_digit) {
+ overflow = false;
+ value = 0.0;
+ }
+ }
}
TINT_ASSERT(Reader, end_ptr == ret.ptr);
diff --git a/src/tint/reader/wgsl/lexer_test.cc b/src/tint/reader/wgsl/lexer_test.cc
index a244273..8d49bf8 100644
--- a/src/tint/reader/wgsl/lexer_test.cc
+++ b/src/tint/reader/wgsl/lexer_test.cc
@@ -492,7 +492,56 @@
// Quantization
FloatData{"3.141592653589793", 3.141592653589793}, // no quantization
FloatData{"3.141592653589793f", 3.1415927410125732}, // f32 quantized
- FloatData{"3.141592653589793h", 3.140625} // f16 quantized
+ FloatData{"3.141592653589793h", 3.140625}, // f16 quantized
+
+ // https://bugs.chromium.org/p/tint/issues/detail?id=1863
+ FloatData{"0."
+ "00000000000000000000000000000000000000000000000000" // 50
+ "00000000000000000000000000000000000000000000000000" // 100
+ "00000000000000000000000000000000000000000000000000" // 150
+ "00000000000000000000000000000000000000000000000000" // 200
+ "00000000000000000000000000000000000000000000000000" // 250
+ "00000000000000000000000000000000000000000000000000" // 300
+ "00000000000000000000000000000000000000000000000000" // 350
+ "1e+0",
+ 0.0},
+ FloatData{"0."
+ "00000000000000000000000000000000000000000000000000" // 50
+ "00000000000000000000000000000000000000000000000000" // 100
+ "00000000000000000000000000000000000000000000000000" // 150
+ "00000000000000000000000000000000000000000000000000" // 200
+ "00000000000000000000000000000000000000000000000000" // 250
+ "00000000000000000000000000000000000000000000000000" // 300
+ "00000000000000000000000000000000000000000000000000" // 350
+ "1e+10",
+ 0.0},
+ FloatData{"0."
+ "00000000000000000000000000000000000000000000000000" // 50
+ "00000000000000000000000000000000000000000000000000" // 100
+ "00000000000000000000000000000000000000000000000000" // 150
+ "00000000000000000000000000000000000000000000000000" // 200
+ "00000000000000000000000000000000000000000000000000" // 250
+ "00000000000000000000000000000000000000000000000000" // 300
+ "00000000000000000000000000000000000000000000000000" // 350
+ "1e+300",
+ 1e-51},
+ FloatData{"0."
+ "00000000000000000000000000000000000000000000000000" // 50
+ "00000000000000000000000000000000000000000000000000" // 100
+ "00000000000000000000000000000000000000000000000000" // 150
+ "00000000000000000000000000000000000000000000000000" // 200
+ "00000000000000000000000000000000000000000000000000" // 250
+ "00000000000000000000000000000000000000000000000000" // 300
+ "00000000000000000000000000000000000000000000000000" // 350
+ "1",
+ 0.0},
+
+ FloatData{"1"
+ "00000000000000000000000000000000000000000000000000" // 50
+ "00000000000000000000000000000000000000000000000000" // 100
+ ".0e-350",
+ 1e-250}
+
));
using FloatTest_Invalid = testing::TestWithParam<const char*>;