[APFloat][ADT] Fix sign handling logic for FMA results that truncate to zero.
authorLang Hames <lhames@gmail.com>
Sun, 4 Jan 2015 01:20:55 +0000 (01:20 +0000)
committerLang Hames <lhames@gmail.com>
Sun, 4 Jan 2015 01:20:55 +0000 (01:20 +0000)
This patch adds a check for underflow when truncating results back to lower
precision at the end of an FMA. The additional sign handling logic in
APFloat::fusedMultiplyAdd should only be performed when the result of the
addition step of the FMA (in full precision) is exactly zero, not when the
result underflows to zero.

Unit tests for this case and related signed zero FMA results are included.

Fixes <rdar://problem/18925551>.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@225123 91177308-0d34-0410-b5e6-96231b3b80d8

lib/Support/APFloat.cpp
unittests/ADT/APFloatTest.cpp

index 295b16c55e207959e682952b8d3f347d23c4eafd..393ecf4784cb4a7fe299696d50b6e64c890c9f1a 100644 (file)
@@ -1823,7 +1823,7 @@ APFloat::fusedMultiplyAdd(const APFloat &multiplicand,
     /* If two numbers add (exactly) to zero, IEEE 754 decrees it is a
        positive zero unless rounding to minus infinity, except that
        adding two like-signed zeroes gives that zero.  */
-    if (category == fcZero && sign != addend.sign)
+    if (category == fcZero && !(fs & opUnderflow) && sign != addend.sign)
       sign = (rounding_mode == rmTowardNegative);
   } else {
     fs = multiplySpecials(multiplicand);
index c7ec16ba803a6cd5c130875b595067cd397484b1..8b82fb2f4ed6f68d4b58267fa6c3ce58f3c92a6d 100644 (file)
@@ -475,6 +475,47 @@ TEST(APFloatTest, FMA) {
     EXPECT_EQ(12.0f, f1.convertToFloat());
   }
 
+  // Test for correct zero sign when answer is exactly zero.
+  // fma(1.0, -1.0, 1.0) -> +ve 0.
+  {
+    APFloat f1(1.0);
+    APFloat f2(-1.0);
+    APFloat f3(1.0);
+    f1.fusedMultiplyAdd(f2, f3, APFloat::rmNearestTiesToEven);
+    EXPECT_TRUE(!f1.isNegative() && f1.isZero());
+  }
+
+  // Test for correct zero sign when answer is exactly zero and rounding towards
+  // negative.
+  // fma(1.0, -1.0, 1.0) -> +ve 0.
+  {
+    APFloat f1(1.0);
+    APFloat f2(-1.0);
+    APFloat f3(1.0);
+    f1.fusedMultiplyAdd(f2, f3, APFloat::rmTowardNegative);
+    EXPECT_TRUE(f1.isNegative() && f1.isZero());
+  }
+
+  // Test for correct (in this case -ve) sign when adding like signed zeros.
+  // Test fma(0.0, -0.0, -0.0) -> -ve 0.
+  {
+    APFloat f1(0.0);
+    APFloat f2(-0.0);
+    APFloat f3(-0.0);
+    f1.fusedMultiplyAdd(f2, f3, APFloat::rmNearestTiesToEven);
+    EXPECT_TRUE(f1.isNegative() && f1.isZero());
+  }
+
+  // Test -ve sign preservation when small negative results underflow.
+  {
+    APFloat f1(APFloat::IEEEdouble,  "-0x1p-1074");
+    APFloat f2(APFloat::IEEEdouble, "+0x1p-1074");
+    APFloat f3(0.0);
+    f1.fusedMultiplyAdd(f2, f3, APFloat::rmNearestTiesToEven);
+    EXPECT_TRUE(f1.isNegative() && f1.isZero());
+  }
+
+  // Test x87 extended precision case from http://llvm.org/PR20728.
   {
     APFloat M1(APFloat::x87DoubleExtended, 1.0);
     APFloat M2(APFloat::x87DoubleExtended, 1.0);