Skip to content

Commit 0097a06

Browse files
Avoid scanning irrelevant truncated fraction
1 parent 27b89da commit 0097a06

2 files changed

Lines changed: 96 additions & 2 deletions

File tree

include/fast_float/digit_comparison.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -285,8 +285,9 @@ parse_mantissa(bigint &result, parsed_number_string_t<UC> &num,
285285
// add the temporary value, then check if we've truncated any digits
286286
add_native(result, limb(powers_of_ten_uint64[counter]), value);
287287
bool truncated = is_truncated(p, pend);
288-
if (num.fraction.ptr != nullptr) {
289-
truncated |= is_truncated(num.fraction);
288+
// A nonzero integer suffix already determines the rounding direction.
289+
if (!truncated && num.fraction.ptr != nullptr) {
290+
truncated = is_truncated(num.fraction);
290291
}
291292
if (truncated) {
292293
round_up_bigint(result, digits);

tests/basictest.cpp

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1153,6 +1153,99 @@ TEST_CASE("double.inf") {
11531153
std::errc::result_out_of_range);
11541154
}
11551155

1156+
TEST_CASE("truncated integer mantissa") {
1157+
constexpr size_t max_digits = fast_float::binary_format<double>::max_digits();
1158+
constexpr size_t integer_digits = max_digits + 1;
1159+
constexpr size_t fraction_length = 1024;
1160+
1161+
auto make_mantissa = [=](char final_integer_digit,
1162+
char final_fraction_digit) {
1163+
std::string input = "1234567890123456789";
1164+
input.append(integer_digits - input.size() - 1, '0');
1165+
input.push_back(final_integer_digit);
1166+
input.push_back('.');
1167+
input.append(fraction_length - 1, '0');
1168+
input.push_back(final_fraction_digit);
1169+
return input;
1170+
};
1171+
1172+
auto parse_mantissa = [=](std::string const &input,
1173+
fast_float::bigint &result) -> size_t {
1174+
fast_float::parse_options options;
1175+
auto number = fast_float::parse_number_string<false>(
1176+
input.data(), input.data() + input.size(), options, true);
1177+
CHECK(number.valid);
1178+
CHECK(number.integer.len() == integer_digits);
1179+
CHECK(number.fraction.len() == fraction_length);
1180+
1181+
size_t digits = 0;
1182+
fast_float::parse_mantissa(result, number, max_digits, digits);
1183+
return digits;
1184+
};
1185+
1186+
auto const nonzero_integer_zero_fraction = make_mantissa('1', '0');
1187+
auto const nonzero_integer_nonzero_fraction = make_mantissa('1', '1');
1188+
fast_float::bigint nonzero_integer_zero_result;
1189+
fast_float::bigint nonzero_integer_nonzero_result;
1190+
CHECK(parse_mantissa(nonzero_integer_zero_fraction,
1191+
nonzero_integer_zero_result) == max_digits + 1);
1192+
CHECK(parse_mantissa(nonzero_integer_nonzero_fraction,
1193+
nonzero_integer_nonzero_result) == max_digits + 1);
1194+
CHECK(nonzero_integer_zero_result.compare(nonzero_integer_nonzero_result) ==
1195+
0);
1196+
1197+
auto const zero_integer_zero_fraction = make_mantissa('0', '0');
1198+
auto const zero_integer_nonzero_fraction = make_mantissa('0', '1');
1199+
fast_float::bigint zero_integer_zero_result;
1200+
fast_float::bigint zero_integer_nonzero_result;
1201+
CHECK(parse_mantissa(zero_integer_zero_fraction, zero_integer_zero_result) ==
1202+
max_digits);
1203+
CHECK(parse_mantissa(zero_integer_nonzero_fraction,
1204+
zero_integer_nonzero_result) == max_digits + 1);
1205+
CHECK(zero_integer_zero_result.compare(zero_integer_nonzero_result) < 0);
1206+
1207+
auto make_exact_conversion_input = [=](char final_fraction_digit) {
1208+
std::string input = "8385788696668661046";
1209+
input.append(integer_digits - input.size() - 1, '0');
1210+
input.push_back('1');
1211+
input.push_back('.');
1212+
input.append(fraction_length - 1, '0');
1213+
input.push_back(final_fraction_digit);
1214+
input += "e-1078";
1215+
return input;
1216+
};
1217+
1218+
auto const exact_input = make_exact_conversion_input('0');
1219+
fast_float::parse_options options;
1220+
auto const number = fast_float::parse_number_string<false>(
1221+
exact_input.data(), exact_input.data() + exact_input.size(), options,
1222+
true);
1223+
REQUIRE(number.too_many_digits);
1224+
REQUIRE(number.integer.len() == integer_digits);
1225+
REQUIRE(number.fraction.len() == fraction_length);
1226+
auto const approximate =
1227+
fast_float::compute_float<fast_float::binary_format<double>>(
1228+
number.exponent, number.mantissa);
1229+
auto const next =
1230+
fast_float::compute_float<fast_float::binary_format<double>>(
1231+
number.exponent, number.mantissa + 1);
1232+
REQUIRE(approximate != next);
1233+
REQUIRE(fast_float::compute_error<fast_float::binary_format<double>>(
1234+
number.exponent, number.mantissa)
1235+
.power2 < 0);
1236+
1237+
for (char final_fraction_digit = '0'; final_fraction_digit <= '1';
1238+
++final_fraction_digit) {
1239+
auto const input = make_exact_conversion_input(final_fraction_digit);
1240+
double value = 0;
1241+
auto const result = fast_float::from_chars(
1242+
input.data(), input.data() + input.size(), value);
1243+
CHECK(result.ec == std::errc());
1244+
CHECK(result.ptr == input.data() + input.size());
1245+
CHECK(value == 0x0.607b00a417628p-1022);
1246+
}
1247+
}
1248+
11561249
TEST_CASE("double.general") {
11571250
verify("0.95000000000000000000", 0.95);
11581251
verify("22250738585072012e-324",

0 commit comments

Comments
 (0)