Skip to content

Commit ab73332

Browse files
committed
[libc][math] Fix incorrect logic in fputil::generic::add_or_sub
Fixes incorrect logic that went unnoticed until the function was tested with output and input types having the same underlying floating-point format. The resulting DyadicFloat's exponent was off by one when adding two subnormal numbers, and the minimum operand's mantissa was misaligned by one bit when adding a normal number with a subnormal number.
1 parent 6c9256d commit ab73332

File tree

2 files changed

+20
-8
lines changed

2 files changed

+20
-8
lines changed

libc/src/__support/FPUtil/generic/add_sub.h

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -160,20 +160,21 @@ add_or_sub(InType x, InType y) {
160160
} else {
161161
InStorageType max_mant = max_bits.get_explicit_mantissa() << GUARD_BITS_LEN;
162162
InStorageType min_mant = min_bits.get_explicit_mantissa() << GUARD_BITS_LEN;
163-
int alignment =
164-
max_bits.get_biased_exponent() - min_bits.get_biased_exponent();
163+
164+
int alignment = (max_bits.get_biased_exponent() - max_bits.is_normal()) -
165+
(min_bits.get_biased_exponent() - min_bits.is_normal());
165166

166167
InStorageType aligned_min_mant =
167168
min_mant >> cpp::min(alignment, RESULT_MANTISSA_LEN);
168169
bool aligned_min_mant_sticky;
169170

170-
if (alignment <= 3)
171+
if (alignment <= GUARD_BITS_LEN)
171172
aligned_min_mant_sticky = false;
172-
else if (alignment <= InFPBits::FRACTION_LEN + 3)
173+
else if (alignment > InFPBits::FRACTION_LEN + GUARD_BITS_LEN)
174+
aligned_min_mant_sticky = true;
175+
else
173176
aligned_min_mant_sticky =
174177
(min_mant << (InFPBits::STORAGE_LEN - alignment)) != 0;
175-
else
176-
aligned_min_mant_sticky = true;
177178

178179
InStorageType min_mant_sticky(static_cast<int>(aligned_min_mant_sticky));
179180

@@ -183,7 +184,7 @@ add_or_sub(InType x, InType y) {
183184
result_mant = max_mant - (aligned_min_mant | min_mant_sticky);
184185
}
185186

186-
int result_exp = max_bits.get_exponent() - RESULT_FRACTION_LEN;
187+
int result_exp = max_bits.get_explicit_exponent() - RESULT_FRACTION_LEN;
187188
DyadicFloat result(result_sign, result_exp, result_mant);
188189
return result.template as<OutType, /*ShouldSignalExceptions=*/true>();
189190
}

libc/test/src/math/smoke/AddTest.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,16 @@ class AddTest : public LIBC_NAMESPACE::testing::FEnvSafeTest {
136136
func(InType(1.0), in.min_denormal);
137137
EXPECT_FP_EXCEPTION(FE_INEXACT);
138138
}
139+
140+
void test_mixed_normality(AddFunc func) {
141+
if (LIBC_NAMESPACE::fputil::get_fp_type<OutType>() !=
142+
LIBC_NAMESPACE::fputil::get_fp_type<InType>())
143+
return;
144+
145+
EXPECT_FP_EQ(FPBits::create_value(Sign::POS, 2U, 0b1U).get_val(),
146+
func(InFPBits::create_value(Sign::POS, 2U, 0U).get_val(),
147+
InFPBits::create_value(Sign::POS, 2U, 0b10U).get_val()));
148+
}
139149
};
140150

141151
#define LIST_ADD_TESTS(OutType, InType, func) \
@@ -145,6 +155,7 @@ class AddTest : public LIBC_NAMESPACE::testing::FEnvSafeTest {
145155
test_invalid_operations(&func); \
146156
} \
147157
TEST_F(LlvmLibcAddTest, RangeErrors) { test_range_errors(&func); } \
148-
TEST_F(LlvmLibcAddTest, InexactResults) { test_inexact_results(&func); }
158+
TEST_F(LlvmLibcAddTest, InexactResults) { test_inexact_results(&func); } \
159+
TEST_F(LlvmLibcAddTest, MixedNormality) { test_mixed_normality(&func); }
149160

150161
#endif // LLVM_LIBC_TEST_SRC_MATH_SMOKE_ADDTEST_H

0 commit comments

Comments
 (0)