[libc-commits] [libc] [libc][math] Fix FP add/sub for signed-zero operands (PR #183243)

Hanliang Xu via libc-commits libc-commits at lists.llvm.org
Fri Feb 27 08:46:19 PST 2026


https://github.com/Hanliang-Xu updated https://github.com/llvm/llvm-project/pull/183243

>From d6cfc11f5e79c0994da29d6209c0cc4f50c4c931 Mon Sep 17 00:00:00 2001
From: Hanliang-Xu <leonxu2004 at outlook.com>
Date: Fri, 27 Feb 2026 11:29:57 -0500
Subject: [PATCH] [libc][math] Fix FP add/sub for signed-zero operands
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

(-0.0) + (-0.0) and (-0.0) - (+0.0) returned +0.0 instead of -0.0.

Ensure these cases comply with IEEE 754 ยง6.3 rule.
---
 libc/src/__support/FPUtil/generic/add_sub.h |  2 ++
 libc/test/src/math/smoke/AddTest.h          | 15 +++++++++++++--
 libc/test/src/math/smoke/SubTest.h          | 15 +++++++++++++--
 3 files changed, 28 insertions(+), 4 deletions(-)

diff --git a/libc/src/__support/FPUtil/generic/add_sub.h b/libc/src/__support/FPUtil/generic/add_sub.h
index 9f3ecff0eb233..4ef9ce06ebf95 100644
--- a/libc/src/__support/FPUtil/generic/add_sub.h
+++ b/libc/src/__support/FPUtil/generic/add_sub.h
@@ -96,6 +96,8 @@ add_or_sub(InType x, InType y) {
 
     if (x_bits.is_zero()) {
       if (y_bits.is_zero()) {
+        if (is_effectively_add)
+          return OutFPBits::zero(x_bits.sign()).get_val();
         switch (quick_get_round()) {
         case FE_DOWNWARD:
           return OutFPBits::zero(Sign::NEG).get_val();
diff --git a/libc/test/src/math/smoke/AddTest.h b/libc/test/src/math/smoke/AddTest.h
index d0a9dcb9c2830..5740ccb743f46 100644
--- a/libc/test/src/math/smoke/AddTest.h
+++ b/libc/test/src/math/smoke/AddTest.h
@@ -165,6 +165,13 @@ class AddTest : public LIBC_NAMESPACE::testing::FEnvSafeTest {
     EXPECT_FP_EQ(OutType(-1.0), func(InType(-2.0), InType(1.0)));
     EXPECT_FP_EQ(OutType(-3.0), func(InType(-2.0), InType(-1.0)));
   }
+
+  void test_signed_zero_result(AddFunc func) {
+    EXPECT_FP_EQ(zero, func(in.zero, in.zero));
+    EXPECT_FP_EQ(zero, func(in.neg_zero, in.zero));
+    EXPECT_FP_EQ(zero, func(in.zero, in.neg_zero));
+    EXPECT_FP_EQ(neg_zero, func(in.neg_zero, in.neg_zero));
+  }
 };
 
 #define LIST_ADD_TESTS(OutType, InType, func)                                  \
@@ -176,7 +183,8 @@ class AddTest : public LIBC_NAMESPACE::testing::FEnvSafeTest {
   TEST_F(LlvmLibcAddTest, RangeErrors) { test_range_errors(&func); }           \
   TEST_F(LlvmLibcAddTest, InexactResults) { test_inexact_results(&func); }     \
   TEST_F(LlvmLibcAddTest, MixedNormality) { test_mixed_normality(&func); }     \
-  TEST_F(LlvmLibcAddTest, MixedSigns) { test_mixed_signs(&func); }
+  TEST_F(LlvmLibcAddTest, MixedSigns) { test_mixed_signs(&func); }             \
+  TEST_F(LlvmLibcAddTest, SignedZeroResult) { test_signed_zero_result(&func); }
 
 #define LIST_ADD_SAME_TYPE_TESTS(suffix, OutType, InType, func)                \
   using LlvmLibcAddTest##suffix = AddTest<OutType, InType>;                    \
@@ -193,6 +201,9 @@ class AddTest : public LIBC_NAMESPACE::testing::FEnvSafeTest {
   TEST_F(LlvmLibcAddTest##suffix, MixedNormality) {                            \
     test_mixed_normality(&func);                                               \
   }                                                                            \
-  TEST_F(LlvmLibcAddTest##suffix, MixedSigns) { test_mixed_signs(&func); }
+  TEST_F(LlvmLibcAddTest##suffix, MixedSigns) { test_mixed_signs(&func); }     \
+  TEST_F(LlvmLibcAddTest##suffix, SignedZeroResult) {                          \
+    test_signed_zero_result(&func);                                            \
+  }
 
 #endif // LLVM_LIBC_TEST_SRC_MATH_SMOKE_ADDTEST_H
diff --git a/libc/test/src/math/smoke/SubTest.h b/libc/test/src/math/smoke/SubTest.h
index 79086aa0bfb72..0f93bb5c2e3e3 100644
--- a/libc/test/src/math/smoke/SubTest.h
+++ b/libc/test/src/math/smoke/SubTest.h
@@ -156,6 +156,13 @@ class SubTest : public LIBC_NAMESPACE::testing::FEnvSafeTest {
     EXPECT_FP_EQ(OutType(-3.0), func(InType(-2.0), InType(1.0)));
     EXPECT_FP_EQ(OutType(-1.0), func(InType(-2.0), InType(-1.0)));
   }
+
+  void test_signed_zero_result(SubFunc func) {
+    EXPECT_FP_EQ(zero, func(in.zero, in.zero));
+    EXPECT_FP_EQ(neg_zero, func(in.neg_zero, in.zero));
+    EXPECT_FP_EQ(zero, func(in.zero, in.neg_zero));
+    EXPECT_FP_EQ(zero, func(in.neg_zero, in.neg_zero));
+  }
 };
 
 #define LIST_SUB_TESTS(OutType, InType, func)                                  \
@@ -166,7 +173,8 @@ class SubTest : public LIBC_NAMESPACE::testing::FEnvSafeTest {
   }                                                                            \
   TEST_F(LlvmLibcSubTest, RangeErrors) { test_range_errors(&func); }           \
   TEST_F(LlvmLibcSubTest, InexactResults) { test_inexact_results(&func); }     \
-  TEST_F(LlvmLibcSubTest, MixedSigns) { test_mixed_signs(&func); }
+  TEST_F(LlvmLibcSubTest, MixedSigns) { test_mixed_signs(&func); }             \
+  TEST_F(LlvmLibcSubTest, SignedZeroResult) { test_signed_zero_result(&func); }
 
 #define LIST_SUB_SAME_TYPE_TESTS(suffix, OutType, InType, func)                \
   using LlvmLibcSubTest##suffix = SubTest<OutType, InType>;                    \
@@ -180,6 +188,9 @@ class SubTest : public LIBC_NAMESPACE::testing::FEnvSafeTest {
   TEST_F(LlvmLibcSubTest##suffix, InexactResults) {                            \
     test_inexact_results(&func);                                               \
   }                                                                            \
-  TEST_F(LlvmLibcSubTest##suffix, MixedSigns) { test_mixed_signs(&func); }
+  TEST_F(LlvmLibcSubTest##suffix, MixedSigns) { test_mixed_signs(&func); }     \
+  TEST_F(LlvmLibcSubTest##suffix, SignedZeroResult) {                          \
+    test_signed_zero_result(&func);                                            \
+  }
 
 #endif // LLVM_LIBC_TEST_SRC_MATH_SMOKE_SUBTEST_H



More information about the libc-commits mailing list