Skip to content

Commit 516c28c

Browse files
committed
Substantial rewrite to eliminate the loop.
Compared to the previous version: The helper function `need_to_round_up` is replaced with `rounding_direction`, which has three rather than two return values: it can distinguish 'round down' from 'already exact, no rounding at all'. The method `DyadicFloat::as_mantissa_type_rounded` no longer returns an overflow indication, because we now avoid overflowing in the first place by early detection of wildly out-of-range exponents. But it does return a rounding direction indicating whether the mantissa was rounded up, down, or was exact. I had to fix `BigInt::decrement()`, which turned out to be accidentally an exact copy of `increment()`. Then `decimal_digits` is rewritten essentially as suggested in the review comments: - there's no loop - the initial estimated exponent is used to detect the need to shift from F to E mode in advance rather than spotting it after the fact by overflow - so now the initial decimal exponent is always either correct or one too small - and if it's one too small, we handle it by truncating a digit off the output decimal mantissa, re-rounding carefully to avoid a double-rounding error.
1 parent 797da34 commit 516c28c

File tree

3 files changed

+242
-147
lines changed

3 files changed

+242
-147
lines changed

libc/src/__support/FPUtil/dyadic_float.h

Lines changed: 44 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -26,35 +26,43 @@
2626
namespace LIBC_NAMESPACE_DECL {
2727
namespace fputil {
2828

29-
// Decide whether to round up a UInt at a given bit position, based on
30-
// the current rounding mode. The assumption is that the caller is
31-
// going to make the integer `value >> rshift`, and then might need to
32-
// round it up by 1 depending on the value of the bits shifted off the
29+
// Decide whether to round a UInt up, down or not at all at a given bit
30+
// position, based on the current rounding mode. The assumption is that the
31+
// caller is going to make the integer `value >> rshift`, and then might need
32+
// to round it up by 1 depending on the value of the bits shifted off the
3333
// bottom.
3434
//
3535
// `logical_sign` causes the behavior of FE_DOWNWARD and FE_UPWARD to
3636
// be reversed, which is what you'd want if this is the mantissa of a
3737
// negative floating-point number.
38+
//
39+
// Return value is +1 if the value should be rounded up; -1 if it should be
40+
// rounded down; 0 if it's exact and needs no rounding.
3841
template <size_t Bits>
39-
LIBC_INLINE constexpr bool
40-
need_to_round_up(const LIBC_NAMESPACE::UInt<Bits> &value, size_t rshift,
41-
Sign logical_sign) {
42+
LIBC_INLINE constexpr int
43+
rounding_direction(const LIBC_NAMESPACE::UInt<Bits> &value, size_t rshift,
44+
Sign logical_sign) {
45+
if (rshift == 0 ||
46+
(rshift < Bits && (value << (Bits - rshift)) == 0) ||
47+
(rshift >= Bits && value == 0))
48+
return 0; // exact
49+
4250
switch (quick_get_round()) {
4351
case FE_TONEAREST:
4452
if (rshift > 0 && rshift <= Bits && value.get_bit(rshift - 1)) {
4553
// We round up, unless the value is an exact halfway case and
4654
// the bit that will end up in the units place is 0, in which
4755
// case tie-break-to-even says round down.
48-
return value.get_bit(rshift) != 0 || (value << (Bits - rshift + 1)) != 0;
56+
return value.get_bit(rshift) != 0 || (value << (Bits - rshift + 1)) != 0 ? +1 : -1;
4957
} else {
50-
return false;
58+
return -1;
5159
}
5260
case FE_TOWARDZERO:
53-
return false;
61+
return -1;
5462
case FE_DOWNWARD:
55-
return logical_sign.is_neg() && (value << (Bits - rshift)) != 0;
63+
return logical_sign.is_neg() && (value << (Bits - rshift)) != 0 ? +1 : -1;
5664
case FE_UPWARD:
57-
return logical_sign.is_pos() && (value << (Bits - rshift)) != 0;
65+
return logical_sign.is_pos() && (value << (Bits - rshift)) != 0 ? +1 : -1;
5866
default:
5967
__builtin_unreachable();
6068
}
@@ -143,7 +151,7 @@ template <size_t Bits> struct DyadicFloat {
143151
const LIBC_NAMESPACE::UInt<MantissaBits> &input_mantissa,
144152
size_t rshift) {
145153
MantissaType result_mantissa(input_mantissa >> rshift);
146-
if (need_to_round_up(input_mantissa, rshift, result_sign)) {
154+
if (rounding_direction(input_mantissa, rshift, result_sign) > 0) {
147155
++result_mantissa;
148156
if (result_mantissa == 0) {
149157
// Rounding up made the mantissa integer wrap round to 0,
@@ -431,25 +439,32 @@ template <size_t Bits> struct DyadicFloat {
431439
}
432440

433441
LIBC_INLINE constexpr MantissaType
434-
as_mantissa_type_rounded(bool *overflowed = nullptr) const {
435-
if (mantissa.is_zero())
436-
return 0;
442+
as_mantissa_type_rounded(int *round_dir_out = nullptr) const {
443+
int round_dir;
444+
MantissaType new_mant;
445+
if (mantissa.is_zero()) {
446+
round_dir = 0;
447+
new_mant = 0;
448+
} else {
449+
new_mant = mantissa;
450+
if (exponent > 0) {
451+
new_mant <<= exponent;
452+
round_dir = 0;
453+
} else if (exponent < 0) {
454+
size_t shift = -exponent;
455+
new_mant >>= shift;
456+
round_dir = rounding_direction(mantissa, shift, sign);
457+
if (round_dir > 0)
458+
++new_mant;
459+
}
437460

438-
MantissaType new_mant = mantissa;
439-
if (exponent > 0) {
440-
new_mant <<= exponent;
441-
if (overflowed)
442-
*overflowed = (new_mant >> exponent) != mantissa;
443-
} else if (exponent < 0) {
444-
size_t shift = -exponent;
445-
new_mant >>= shift;
446-
if (need_to_round_up(mantissa, shift, sign))
447-
++new_mant;
461+
if (sign.is_neg()) {
462+
new_mant = (~new_mant) + 1;
463+
}
448464
}
449465

450-
if (sign.is_neg()) {
451-
new_mant = (~new_mant) + 1;
452-
}
466+
if (round_dir_out)
467+
*round_dir_out = round_dir;
453468

454469
return new_mant;
455470
}

libc/src/__support/big_int.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -980,7 +980,7 @@ struct BigInt {
980980
}
981981

982982
LIBC_INLINE constexpr void decrement() {
983-
multiword::add_with_carry(val, cpp::array<WordType, 1>{1});
983+
multiword::sub_with_borrow(val, cpp::array<WordType, 1>{1});
984984
}
985985

986986
LIBC_INLINE constexpr void extend(size_t index, bool is_neg) {

0 commit comments

Comments
 (0)