Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 30 additions & 13 deletions double-conversion/string-to-double.cc
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,14 @@ static double RadixStringToIeee(Iterator* current,

const int kDoubleSize = Double::kSignificandSize;
const int kSingleSize = Single::kSignificandSize;
const int kSignificandSize = read_as_double? kDoubleSize: kSingleSize;
// A hex-float is formed here as a double and rounded to float by the caller
// (StringToFloat casts the result). Rounding the significand to single
// precision here would double-round both subnormal floats and floats whose
// exact significand exceeds 53 bits, so keep the full double significand and
// round it to odd, which makes that final single-precision cast correct.
const bool round_hex_float_to_single = parse_as_hex_float && !read_as_double;
const int kSignificandSize =
(read_as_double || parse_as_hex_float) ? kDoubleSize : kSingleSize;

*result_is_junk = true;

Expand Down Expand Up @@ -375,21 +382,31 @@ static double RadixStringToIeee(Iterator* current,
return junk_string_value;
}

int middle_value = (1 << (overflow_bits_count - 1));
if (dropped_bits > middle_value) {
number++; // Rounding up.
} else if (dropped_bits == middle_value) {
// Rounding to even to consistency with decimals: half-way case rounds
// up if significant part is odd and down otherwise.
if ((number & 1) != 0 || !zero_tail) {
if (round_hex_float_to_single) {
// Round the significand to odd: set the lowest kept bit whenever any
// bit was dropped. The caller rounds this double to float; rounding to
// nearest here would double-round, but round-to-odd leaves that final
// single rounding correct for normal and subnormal results alike.
if (dropped_bits != 0 || !zero_tail) {
number |= 1;
}
} else {
int middle_value = (1 << (overflow_bits_count - 1));
if (dropped_bits > middle_value) {
number++; // Rounding up.
} else if (dropped_bits == middle_value) {
// Rounding to even to consistency with decimals: half-way case rounds
// up if significant part is odd and down otherwise.
if ((number & 1) != 0 || !zero_tail) {
number++; // Rounding up.
}
}
}

// Rounding up may cause overflow.
if ((number & ((int64_t)1 << kSignificandSize)) != 0) {
exponent++;
number >>= 1;
// Rounding up may cause overflow.
if ((number & ((int64_t)1 << kSignificandSize)) != 0) {
exponent++;
number >>= 1;
}
}
break;
}
Expand Down
23 changes: 23 additions & 0 deletions test/cctest/test-conversions.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5364,6 +5364,29 @@ TEST(StringToFloatHexString) {
CHECK_EQ(0.0f, StrToF("0x1.p-10000000000000000", flags, 0.0,
&processed, &all_used));
CHECK(all_used);

// Hex-floats must be rounded straight to float, without an intermediate
// rounding to a double significand. Subnormal results carry fewer than 24
// significand bits, so the value below rounds up when taken directly to
// float but rounds down if first rounded to a 24-bit normal significand.
CHECK_EQ(Single(0x001149a1u).value(),
StrToF("0x8a4.d047p-140", flags, 0.0, &processed, &all_used));
CHECK(all_used);

CHECK_EQ(Single(0x002f3e11u).value(),
StrToF("0x2f3.e10ap-137", flags, 0.0, &processed, &all_used));
CHECK(all_used);

// The double-rounding examples from the hex-integer cases above, written as
// hex-floats. Rounding the significand to a double first would land on
// 72057594037927936.0f; the direct single rounding gives the value below.
CHECK_EQ(72057602627862528.0f,
StrToF("0x100000100000008p0", flags, 0.0, &processed, &all_used));
CHECK(all_used);

CHECK_EQ(72057602627862528.0f,
StrToF("0x1000002FFFFFFF8p0", flags, 0.0, &processed, &all_used));
CHECK(all_used);
}


Expand Down