aboutsummaryrefslogtreecommitdiff
path: root/llvm/lib/Support/APFloat.cpp
diff options
context:
space:
mode:
authorbeetrees <b@beetr.ee>2025-03-10 02:44:27 +0000
committerGitHub <noreply@github.com>2025-03-10 09:44:27 +0700
commit8b9c91ec7da0ca3daab8ff6711126443e500bb66 (patch)
tree2fd76fd6a9e9ea442b149058960ce4685d1d35c1 /llvm/lib/Support/APFloat.cpp
parent82f2b661101d1133b72872141009035f01a42595 (diff)
downloadllvm-8b9c91ec7da0ca3daab8ff6711126443e500bb66.zip
llvm-8b9c91ec7da0ca3daab8ff6711126443e500bb66.tar.gz
llvm-8b9c91ec7da0ca3daab8ff6711126443e500bb66.tar.bz2
[APFloat] Fix `IEEEFloat::addOrSubtractSignificand` and `IEEEFloat::normalize` (#98721)
Fixes #63895 Fixes #104984 Before this PR, `addOrSubtractSignificand` presumed that the loss came from the side being subtracted, and didn't handle the case where lhs == rhs and there was loss. This can occur during FMA. This PR fixes the situation by correctly determining where the loss came from and handling it appropriately. Additionally, `normalize` failed to adjust the exponent when the significand is zero but `lost_fraction != lfExactlyZero`. This meant that the test case from #63895 was rounded incorrectly as the loss wasn't adjusted to account for the exponent being below the minimum exponent. This PR fixes this by only skipping the exponent adjustment if the significand is zero and there was no lost fraction. (Note to reviewer: I don't have commit access)
Diffstat (limited to 'llvm/lib/Support/APFloat.cpp')
-rw-r--r--llvm/lib/Support/APFloat.cpp50
1 files changed, 35 insertions, 15 deletions
diff --git a/llvm/lib/Support/APFloat.cpp b/llvm/lib/Support/APFloat.cpp
index cfc3c3b..e058010 100644
--- a/llvm/lib/Support/APFloat.cpp
+++ b/llvm/lib/Support/APFloat.cpp
@@ -1657,7 +1657,8 @@ APFloat::opStatus IEEEFloat::normalize(roundingMode rounding_mode,
/* Before rounding normalize the exponent of fcNormal numbers. */
omsb = significandMSB() + 1;
- if (omsb) {
+ // Only skip this `if` if the value is exactly zero.
+ if (omsb || lost_fraction != lfExactlyZero) {
/* OMSB is numbered from 1. We want to place it in the integer
bit numbered PRECISION if possible, with a compensating change in
the exponent. */
@@ -1839,7 +1840,7 @@ APFloat::opStatus IEEEFloat::addOrSubtractSpecials(const IEEEFloat &rhs,
/* Add or subtract two normal numbers. */
lostFraction IEEEFloat::addOrSubtractSignificand(const IEEEFloat &rhs,
bool subtract) {
- integerPart carry;
+ integerPart carry = 0;
lostFraction lost_fraction;
int bits;
@@ -1857,11 +1858,13 @@ lostFraction IEEEFloat::addOrSubtractSignificand(const IEEEFloat &rhs,
"This floating point format does not support signed values");
IEEEFloat temp_rhs(rhs);
+ bool lost_fraction_is_from_rhs = false;
if (bits == 0)
lost_fraction = lfExactlyZero;
else if (bits > 0) {
lost_fraction = temp_rhs.shiftSignificandRight(bits - 1);
+ lost_fraction_is_from_rhs = true;
shiftSignificandLeft(1);
} else {
lost_fraction = shiftSignificandRight(-bits - 1);
@@ -1869,23 +1872,40 @@ lostFraction IEEEFloat::addOrSubtractSignificand(const IEEEFloat &rhs,
}
// Should we reverse the subtraction.
- if (compareAbsoluteValue(temp_rhs) == cmpLessThan) {
- carry = temp_rhs.subtractSignificand
- (*this, lost_fraction != lfExactlyZero);
+ cmpResult cmp_result = compareAbsoluteValue(temp_rhs);
+ if (cmp_result == cmpLessThan) {
+ bool borrow =
+ lost_fraction != lfExactlyZero && !lost_fraction_is_from_rhs;
+ if (borrow) {
+ // The lost fraction is being subtracted, borrow from the significand
+ // and invert `lost_fraction`.
+ if (lost_fraction == lfLessThanHalf)
+ lost_fraction = lfMoreThanHalf;
+ else if (lost_fraction == lfMoreThanHalf)
+ lost_fraction = lfLessThanHalf;
+ }
+ carry = temp_rhs.subtractSignificand(*this, borrow);
copySignificand(temp_rhs);
sign = !sign;
- } else {
- carry = subtractSignificand
- (temp_rhs, lost_fraction != lfExactlyZero);
+ } else if (cmp_result == cmpGreaterThan) {
+ bool borrow = lost_fraction != lfExactlyZero && lost_fraction_is_from_rhs;
+ if (borrow) {
+ // The lost fraction is being subtracted, borrow from the significand
+ // and invert `lost_fraction`.
+ if (lost_fraction == lfLessThanHalf)
+ lost_fraction = lfMoreThanHalf;
+ else if (lost_fraction == lfMoreThanHalf)
+ lost_fraction = lfLessThanHalf;
+ }
+ carry = subtractSignificand(temp_rhs, borrow);
+ } else { // cmpEqual
+ zeroSignificand();
+ if (lost_fraction != lfExactlyZero && lost_fraction_is_from_rhs) {
+ // rhs is slightly larger due to the lost fraction, flip the sign.
+ sign = !sign;
+ }
}
- /* Invert the lost fraction - it was on the RHS and
- subtracted. */
- if (lost_fraction == lfLessThanHalf)
- lost_fraction = lfMoreThanHalf;
- else if (lost_fraction == lfMoreThanHalf)
- lost_fraction = lfLessThanHalf;
-
/* The code above is intended to ensure that no borrow is
necessary. */
assert(!carry);