[libc-commits] [libc] [libc][math] Use exact exclusive upper bounds for fromfp conversions (PR #236477)
via libc-commits
libc-commits at lists.llvm.org
Wed Oct 28 20:25:21 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-libc
Author: sriramshastry
<details>
<summary>Changes</summary>
Use exactly representable exclusive upper power-of-two bounds in the shared
fromfp range check. Avoid accepting the rounded-up endpoint or raising FE_INEXACT
during bound construction. Add wide-width regressions for the signed and
unsigned fromfp and fromfpx families.
## Validation
Tested independently at ea08830d976fbe61384695d33e0acafee8d88737 on x86-64 Linux,
Release, GCC 13 and Clang 18. With only the revised tests, 28/28 smoke target
configurations fail per compiler; with the repair, 28/28 pass. All four families check
that zero does not acquire inexact while constructing the bound, and that the exact
exclusive upper bound is invalid. Changed-hunk formatting and whitespace checks
pass. No ABI or return-type change is proposed.
## Scope and Review
Check exact exclusive signed/unsigned bounds and exception-free bound construction
across all four families. No public ABI or return-type change is proposed.
## Updated submission base
Submission base: 10c142fa799e258285b6cfb8fce153864586152d.
The standalone baseline/patched results above remain recorded at their original
tested snapshot. At this newer base, the fourteen-only combined suite passes
150 native configurations per compiler and twelve four-mode numerical runs,
with zero value or scoped-reporting mismatches.
Signed-off-by: sriramshastry <sriramshastry@<!-- -->gmail.com>
---
Full diff: https://github.com/llvm/llvm-project/pull/236477.diff
5 Files Affected:
- (modified) libc/src/__support/FPUtil/NearestIntegerOperations.h (+7-8)
- (modified) libc/test/src/math/smoke/FromfpTest.h (+20)
- (modified) libc/test/src/math/smoke/FromfpxTest.h (+20)
- (modified) libc/test/src/math/smoke/UfromfpTest.h (+20)
- (modified) libc/test/src/math/smoke/UfromfpxTest.h (+20)
``````````diff
diff --git a/libc/src/__support/FPUtil/NearestIntegerOperations.h b/libc/src/__support/FPUtil/NearestIntegerOperations.h
index 156a6de255621b..d322d7d9df0b87 100644
--- a/libc/src/__support/FPUtil/NearestIntegerOperations.h
+++ b/libc/src/__support/FPUtil/NearestIntegerOperations.h
@@ -298,11 +298,11 @@ fromfp(T x, int rnd, unsigned int width) {
raise_except_if_required(FE_INVALID);
return FPBits<T>::quiet_nan().get_val();
}
- // rounded_value > 2^(width - 1) - 1
+ // Use the exclusive upper bound: subtracting one can round back up to
+ // the bound when width exceeds the precision of T.
T range_max =
- FPBits<T>::create_value(Sign::POS, range_exp, EXPLICIT_BIT).get_val() -
- T(1.0);
- if (rounded_value > range_max) {
+ FPBits<T>::create_value(Sign::POS, range_exp, EXPLICIT_BIT).get_val();
+ if (rounded_value >= range_max) {
raise_except_if_required(FE_INVALID);
return FPBits<T>::quiet_nan().get_val();
}
@@ -320,11 +320,10 @@ fromfp(T x, int rnd, unsigned int width) {
return rounded_value;
StorageType range_exp = static_cast<StorageType>(width + FPBits<T>::EXP_BIAS);
- // rounded_value > 2^width - 1
+ // Check the exactly representable exclusive upper bound.
T range_max =
- FPBits<T>::create_value(Sign::POS, range_exp, EXPLICIT_BIT).get_val() -
- T(1.0);
- if (rounded_value > range_max) {
+ FPBits<T>::create_value(Sign::POS, range_exp, EXPLICIT_BIT).get_val();
+ if (rounded_value >= range_max) {
raise_except_if_required(FE_INVALID);
return FPBits<T>::quiet_nan().get_val();
}
diff --git a/libc/test/src/math/smoke/FromfpTest.h b/libc/test/src/math/smoke/FromfpTest.h
index fe78c9050a7fe6..8dd9ae422cbdbb 100644
--- a/libc/test/src/math/smoke/FromfpTest.h
+++ b/libc/test/src/math/smoke/FromfpTest.h
@@ -469,10 +469,30 @@ class FromfpTestTemplate : public LIBC_NAMESPACE::testing::FEnvSafeTest {
EXPECT_FP_EQ_WITH_EXCEPTION(
aNaN, func(T(-5.75), UNKNOWN_MATH_ROUNDING_DIRECTION, 3U), FE_INVALID);
}
+ void testWideRangeBoundary(FromfpFunc func) {
+ using namespace LIBC_NAMESPACE::fputil::testing;
+ ForceRoundingMode rounding(RoundingMode::Nearest);
+ if (!rounding.success)
+ return;
+ constexpr unsigned EXPONENT = FPBits::FRACTION_LEN + 3;
+ constexpr unsigned WIDTH = EXPONENT + 1;
+ FPBits limit = FPBits::one();
+ limit.set_biased_exponent(FPBits::EXP_BIAS + EXPONENT);
+ T boundary = limit.get_val();
+ // Computing a range bound must not raise inexact for an exact zero input.
+ EXPECT_FP_EQ_WITH_EXCEPTION(zero, func(zero, FP_INT_TOWARDZERO, WIDTH), 0);
+ EXPECT_MATH_ERRNO(0);
+ EXPECT_FP_EQ_WITH_EXCEPTION(aNaN, func(boundary, FP_INT_TOWARDZERO, WIDTH),
+ FE_INVALID);
+ EXPECT_MATH_ERRNO(0);
+ }
};
#define LIST_FROMFP_TESTS(Name, T, func) \
using LlvmLibc##Name##Test = FromfpTestTemplate<T>; \
+ TEST_F(LlvmLibc##Name##Test, WideRangeBoundary) { \
+ testWideRangeBoundary(&func); \
+ } \
TEST_F(LlvmLibc##Name##Test, SpecialNumbersNonzeroWidth) { \
testSpecialNumbersNonzeroWidth(&func); \
} \
diff --git a/libc/test/src/math/smoke/FromfpxTest.h b/libc/test/src/math/smoke/FromfpxTest.h
index 5696ad16520a73..8fad473370c6f1 100644
--- a/libc/test/src/math/smoke/FromfpxTest.h
+++ b/libc/test/src/math/smoke/FromfpxTest.h
@@ -631,10 +631,30 @@ class FromfpxTestTemplate : public LIBC_NAMESPACE::testing::FEnvSafeTest {
EXPECT_FP_EQ_WITH_EXCEPTION(
aNaN, func(T(-5.75), UNKNOWN_MATH_ROUNDING_DIRECTION, 3U), FE_INVALID);
}
+ void testWideRangeBoundary(FromfpxFunc func) {
+ using namespace LIBC_NAMESPACE::fputil::testing;
+ ForceRoundingMode rounding(RoundingMode::Nearest);
+ if (!rounding.success)
+ return;
+ constexpr unsigned EXPONENT = FPBits::FRACTION_LEN + 3;
+ constexpr unsigned WIDTH = EXPONENT + 1;
+ FPBits limit = FPBits::one();
+ limit.set_biased_exponent(FPBits::EXP_BIAS + EXPONENT);
+ T boundary = limit.get_val();
+ // Computing a range bound must not raise inexact for an exact zero input.
+ EXPECT_FP_EQ_WITH_EXCEPTION(zero, func(zero, FP_INT_TOWARDZERO, WIDTH), 0);
+ EXPECT_MATH_ERRNO(0);
+ EXPECT_FP_EQ_WITH_EXCEPTION(aNaN, func(boundary, FP_INT_TOWARDZERO, WIDTH),
+ FE_INVALID);
+ EXPECT_MATH_ERRNO(0);
+ }
};
#define LIST_FROMFPX_TESTS(Name, T, func) \
using LlvmLibc##Name##Test = FromfpxTestTemplate<T>; \
+ TEST_F(LlvmLibc##Name##Test, WideRangeBoundary) { \
+ testWideRangeBoundary(&func); \
+ } \
TEST_F(LlvmLibc##Name##Test, SpecialNumbersNonzeroWidth) { \
testSpecialNumbersNonzeroWidth(&func); \
} \
diff --git a/libc/test/src/math/smoke/UfromfpTest.h b/libc/test/src/math/smoke/UfromfpTest.h
index 6ec720e0fa6c46..8775e1e9e5f1c6 100644
--- a/libc/test/src/math/smoke/UfromfpTest.h
+++ b/libc/test/src/math/smoke/UfromfpTest.h
@@ -403,10 +403,30 @@ class UfromfpTestTemplate : public LIBC_NAMESPACE::testing::FEnvSafeTest {
EXPECT_FP_EQ_WITH_EXCEPTION(
aNaN, func(T(-5.75), UNKNOWN_MATH_ROUNDING_DIRECTION, 32U), FE_INVALID);
}
+ void testWideRangeBoundary(UfromfpFunc func) {
+ using namespace LIBC_NAMESPACE::fputil::testing;
+ ForceRoundingMode rounding(RoundingMode::Nearest);
+ if (!rounding.success)
+ return;
+ constexpr unsigned EXPONENT = FPBits::FRACTION_LEN + 3;
+ constexpr unsigned WIDTH = EXPONENT;
+ FPBits limit = FPBits::one();
+ limit.set_biased_exponent(FPBits::EXP_BIAS + EXPONENT);
+ T boundary = limit.get_val();
+ // Computing a range bound must not raise inexact for an exact zero input.
+ EXPECT_FP_EQ_WITH_EXCEPTION(zero, func(zero, FP_INT_TOWARDZERO, WIDTH), 0);
+ EXPECT_MATH_ERRNO(0);
+ EXPECT_FP_EQ_WITH_EXCEPTION(aNaN, func(boundary, FP_INT_TOWARDZERO, WIDTH),
+ FE_INVALID);
+ EXPECT_MATH_ERRNO(0);
+ }
};
#define LIST_UFROMFP_TESTS(Name, T, func) \
using LlvmLibc##Name##Test = UfromfpTestTemplate<T>; \
+ TEST_F(LlvmLibc##Name##Test, WideRangeBoundary) { \
+ testWideRangeBoundary(&func); \
+ } \
TEST_F(LlvmLibc##Name##Test, SpecialNumbersNonzeroWidth) { \
testSpecialNumbersNonzeroWidth(&func); \
} \
diff --git a/libc/test/src/math/smoke/UfromfpxTest.h b/libc/test/src/math/smoke/UfromfpxTest.h
index 689ff543c7a8dc..0a1e69f1d681e9 100644
--- a/libc/test/src/math/smoke/UfromfpxTest.h
+++ b/libc/test/src/math/smoke/UfromfpxTest.h
@@ -494,10 +494,30 @@ class UfromfpxTestTemplate : public LIBC_NAMESPACE::testing::FEnvSafeTest {
EXPECT_FP_EQ_WITH_EXCEPTION(
aNaN, func(T(-5.75), UNKNOWN_MATH_ROUNDING_DIRECTION, 32U), FE_INVALID);
}
+ void testWideRangeBoundary(UfromfpxFunc func) {
+ using namespace LIBC_NAMESPACE::fputil::testing;
+ ForceRoundingMode rounding(RoundingMode::Nearest);
+ if (!rounding.success)
+ return;
+ constexpr unsigned EXPONENT = FPBits::FRACTION_LEN + 3;
+ constexpr unsigned WIDTH = EXPONENT;
+ FPBits limit = FPBits::one();
+ limit.set_biased_exponent(FPBits::EXP_BIAS + EXPONENT);
+ T boundary = limit.get_val();
+ // Computing a range bound must not raise inexact for an exact zero input.
+ EXPECT_FP_EQ_WITH_EXCEPTION(zero, func(zero, FP_INT_TOWARDZERO, WIDTH), 0);
+ EXPECT_MATH_ERRNO(0);
+ EXPECT_FP_EQ_WITH_EXCEPTION(aNaN, func(boundary, FP_INT_TOWARDZERO, WIDTH),
+ FE_INVALID);
+ EXPECT_MATH_ERRNO(0);
+ }
};
#define LIST_UFROMFPX_TESTS(Name, T, func) \
using LlvmLibc##Name##Test = UfromfpxTestTemplate<T>; \
+ TEST_F(LlvmLibc##Name##Test, WideRangeBoundary) { \
+ testWideRangeBoundary(&func); \
+ } \
TEST_F(LlvmLibc##Name##Test, SpecialNumbersNonzeroWidth) { \
testSpecialNumbersNonzeroWidth(&func); \
} \
``````````
</details>
https://github.com/llvm/llvm-project/pull/236477
More information about the libc-commits
mailing list