[llvm] r181715 - Fix a bug that APFloat::fusedMultiplyAdd() mistakenly evaluate "14.5f * -14.5f + 225.0f" to 225.0f.

Shuxin Yang shuxin.llvm at gmail.com
Mon May 13 11:03:12 PDT 2013


Author: shuxin_yang
Date: Mon May 13 13:03:12 2013
New Revision: 181715

URL: http://llvm.org/viewvc/llvm-project?rev=181715&view=rev
Log:
Fix a bug that APFloat::fusedMultiplyAdd() mistakenly evaluate "14.5f * -14.5f + 225.0f" to 225.0f.

Modified:
    llvm/trunk/lib/Support/APFloat.cpp
    llvm/trunk/unittests/ADT/APFloatTest.cpp

Modified: llvm/trunk/lib/Support/APFloat.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Support/APFloat.cpp?rev=181715&r1=181714&r2=181715&view=diff
==============================================================================
--- llvm/trunk/lib/Support/APFloat.cpp (original)
+++ llvm/trunk/lib/Support/APFloat.cpp Mon May 13 13:03:12 2013
@@ -872,7 +872,21 @@ APFloat::multiplySignificand(const APFlo
   omsb = APInt::tcMSB(fullSignificand, newPartsCount) + 1;
   exponent += rhs.exponent;
 
+  // Assume the operands involved in the multiplication are single-precision
+  // FP, and the two multiplicants are:
+  //   *this = a23 . a22 ... a0 * 2^e1
+  //     rhs = b23 . b22 ... b0 * 2^e2
+  // the result of multiplication is:
+  //   *this = c47 c46 . c45 ... c0 * 2^(e1+e2)
+  // Note that there are two significant bits at the left-hand side of the 
+  // radix point. Move the radix point toward left by one bit, and adjust
+  // exponent accordingly.
+  exponent += 1;
+
   if (addend) {
+    // The intermediate result of the multiplication has "2 * precision" 
+    // signicant bit; adjust the addend to be consistent with mul result.
+    //
     Significand savedSignificand = significand;
     const fltSemantics *savedSemantics = semantics;
     fltSemantics extendedSemantics;
@@ -880,8 +894,9 @@ APFloat::multiplySignificand(const APFlo
     unsigned int extendedPrecision;
 
     /* Normalize our MSB.  */
-    extendedPrecision = precision + precision - 1;
+    extendedPrecision = 2 * precision;
     if (omsb != extendedPrecision) {
+      assert(extendedPrecision > omsb);
       APInt::tcShiftLeft(fullSignificand, newPartsCount,
                          extendedPrecision - omsb);
       exponent -= extendedPrecision - omsb;
@@ -912,8 +927,18 @@ APFloat::multiplySignificand(const APFlo
     omsb = APInt::tcMSB(fullSignificand, newPartsCount) + 1;
   }
 
-  exponent -= (precision - 1);
-
+  // Convert the result having "2 * precision" significant-bits back to the one
+  // having "precision" significant-bits. First, move the radix point from 
+  // poision "2*precision - 1" to "precision - 1". The exponent need to be
+  // adjusted by "2*precision - 1" - "precision - 1" = "precision".
+  exponent -= precision;
+
+  // In case MSB resides at the left-hand side of radix point, shift the
+  // mantissa right by some amount to make sure the MSB reside right before
+  // the radix point (i.e. "MSB . rest-significant-bits").
+  //
+  // Note that the result is not normalized when "omsb < precision". So, the
+  // caller needs to call APFloat::normalize() if normalized value is expected.
   if (omsb > precision) {
     unsigned int bits, significantParts;
     lostFraction lf;

Modified: llvm/trunk/unittests/ADT/APFloatTest.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/ADT/APFloatTest.cpp?rev=181715&r1=181714&r2=181715&view=diff
==============================================================================
--- llvm/trunk/unittests/ADT/APFloatTest.cpp (original)
+++ llvm/trunk/unittests/ADT/APFloatTest.cpp Mon May 13 13:03:12 2013
@@ -33,6 +33,29 @@ static std::string convertToString(doubl
 
 namespace {
 
+TEST(APFloatTest, FMA) {
+  APFloat::roundingMode rdmd = APFloat::rmNearestTiesToEven;
+
+  {
+    APFloat f1(14.5f);
+    APFloat f2(-14.5f);
+    APFloat f3(225.0f);
+    f1.fusedMultiplyAdd(f2, f3, APFloat::rmNearestTiesToEven);
+    EXPECT_EQ(14.75f, f1.convertToFloat());
+  }
+
+  {
+    APFloat Val2(2.0f);
+    APFloat f1((float)1.17549435e-38F);
+    APFloat f2((float)1.17549435e-38F);
+    f1.divide(Val2, rdmd);
+    f2.divide(Val2, rdmd);
+    APFloat f3(12.0f);
+    f1.fusedMultiplyAdd(f2, f3, APFloat::rmNearestTiesToEven);
+    EXPECT_EQ(12.0f, f1.convertToFloat());
+  }
+}
+
 TEST(APFloatTest, Denormal) {
   APFloat::roundingMode rdmd = APFloat::rmNearestTiesToEven;
 





More information about the llvm-commits mailing list