[llvm] r263369 - APFloat: Fix scalbn handling of denormals

Matt Arsenault via llvm-commits llvm-commits at lists.llvm.org
Sat Mar 12 21:11:51 PST 2016


Author: arsenm
Date: Sat Mar 12 23:11:51 2016
New Revision: 263369

URL: http://llvm.org/viewvc/llvm-project?rev=263369&view=rev
Log:
APFloat: Fix scalbn handling of denormals

This was incorrect for denormals, and also failed
on longer exponent ranges.

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

Modified: llvm/trunk/include/llvm/ADT/APFloat.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/ADT/APFloat.h?rev=263369&r1=263368&r2=263369&view=diff
==============================================================================
--- llvm/trunk/include/llvm/ADT/APFloat.h (original)
+++ llvm/trunk/include/llvm/ADT/APFloat.h Sat Mar 12 23:11:51 2016
@@ -523,7 +523,7 @@ public:
   }
 
   /// \brief Returns: X * 2^Exp for integral exponents.
-  friend APFloat scalbn(APFloat X, int Exp);
+  friend APFloat scalbn(APFloat X, int Exp, roundingMode);
 
 private:
 
@@ -651,7 +651,7 @@ private:
 /// These additional declarations are required in order to compile LLVM with IBM
 /// xlC compiler.
 hash_code hash_value(const APFloat &Arg);
-APFloat scalbn(APFloat X, int Exp);
+APFloat scalbn(APFloat X, int Exp, APFloat::roundingMode);
 
 /// \brief Returns the absolute value of the argument.
 inline APFloat abs(APFloat X) {

Modified: llvm/trunk/lib/Support/APFloat.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Support/APFloat.cpp?rev=263369&r1=263368&r2=263369&view=diff
==============================================================================
--- llvm/trunk/lib/Support/APFloat.cpp (original)
+++ llvm/trunk/lib/Support/APFloat.cpp Sat Mar 12 23:11:51 2016
@@ -3945,19 +3945,21 @@ APFloat::makeZero(bool Negative) {
   APInt::tcSet(significandParts(), 0, partCount());  
 }
 
-APFloat llvm::scalbn(APFloat X, int Exp) {
-  if (X.isInfinity() || X.isZero() || X.isNaN())
-    return X;
-
+APFloat llvm::scalbn(APFloat X, int Exp, APFloat::roundingMode RoundingMode) {
   auto MaxExp = X.getSemantics().maxExponent;
   auto MinExp = X.getSemantics().minExponent;
-  if (Exp > (MaxExp - X.exponent))
-    // Overflow saturates to infinity.
-    return APFloat::getInf(X.getSemantics(), X.isNegative());
-  if (Exp < (MinExp - X.exponent))
-    // Underflow saturates to zero.
-    return APFloat::getZero(X.getSemantics(), X.isNegative());
 
-  X.exponent += Exp;
+  // If Exp is wildly out-of-scale, simply adding it to X.exponent will
+  // overflow; clamp it to a safe range before adding, but ensure that the range
+  // is large enough that the clamp does not change the result. The range we
+  // need to support is the difference between the largest possible exponent and
+  // the normalized exponent of half the smallest denormal.
+
+  int SignificandBits = X.getSemantics().precision - 1;
+  int MaxIncrement = MaxExp - (MinExp - SignificandBits) + 1;
+
+  // Clamp to one past the range ends to let normalize handle overlflow.
+  X.exponent += std::min(std::max(Exp, -MaxIncrement - 1), MaxIncrement);
+  X.normalize(RoundingMode, lfExactlyZero);
   return X;
 }

Modified: llvm/trunk/unittests/ADT/APFloatTest.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/ADT/APFloatTest.cpp?rev=263369&r1=263368&r2=263369&view=diff
==============================================================================
--- llvm/trunk/unittests/ADT/APFloatTest.cpp (original)
+++ llvm/trunk/unittests/ADT/APFloatTest.cpp Sat Mar 12 23:11:51 2016
@@ -140,14 +140,14 @@ TEST(APFloatTest, next) {
   test = APFloat(APFloat::IEEEquad, "0x0.0000000000000000000000000001p-16382");
   expected = APFloat::getZero(APFloat::IEEEquad, false);
   EXPECT_EQ(test.next(true), APFloat::opOK);
-  EXPECT_TRUE(test.isZero() && !test.isNegative());
+  EXPECT_TRUE(test.isPosZero());
   EXPECT_TRUE(test.bitwiseIsEqual(expected));
 
   // nextUp(-getSmallest()) = -0.
   test = APFloat(APFloat::IEEEquad, "-0x0.0000000000000000000000000001p-16382");
   expected = APFloat::getZero(APFloat::IEEEquad, true);
   EXPECT_EQ(test.next(false), APFloat::opOK);
-  EXPECT_TRUE(test.isZero() && test.isNegative());
+  EXPECT_TRUE(test.isNegZero());
   EXPECT_TRUE(test.bitwiseIsEqual(expected));
 
   // nextDown(-getSmallest()) = -nextUp(getSmallest()) = -getSmallest() - inc.
@@ -2850,15 +2850,17 @@ TEST(APFloatTest, ilogb) {
 }
 
 TEST(APFloatTest, scalbn) {
+
+  const APFloat::roundingMode RM = APFloat::rmNearestTiesToEven;
   EXPECT_TRUE(
       APFloat(APFloat::IEEEsingle, "0x1p+0")
-          .bitwiseIsEqual(scalbn(APFloat(APFloat::IEEEsingle, "0x1p+0"), 0)));
+      .bitwiseIsEqual(scalbn(APFloat(APFloat::IEEEsingle, "0x1p+0"), 0, RM)));
   EXPECT_TRUE(
       APFloat(APFloat::IEEEsingle, "0x1p+42")
-          .bitwiseIsEqual(scalbn(APFloat(APFloat::IEEEsingle, "0x1p+0"), 42)));
+      .bitwiseIsEqual(scalbn(APFloat(APFloat::IEEEsingle, "0x1p+0"), 42, RM)));
   EXPECT_TRUE(
       APFloat(APFloat::IEEEsingle, "0x1p-42")
-          .bitwiseIsEqual(scalbn(APFloat(APFloat::IEEEsingle, "0x1p+0"), -42)));
+      .bitwiseIsEqual(scalbn(APFloat(APFloat::IEEEsingle, "0x1p+0"), -42, RM)));
 
   APFloat PInf = APFloat::getInf(APFloat::IEEEsingle, false);
   APFloat MInf = APFloat::getInf(APFloat::IEEEsingle, true);
@@ -2868,27 +2870,138 @@ TEST(APFloatTest, scalbn) {
   APFloat QMNaN = APFloat::getNaN(APFloat::IEEEsingle, true);
   APFloat SNaN = APFloat::getSNaN(APFloat::IEEEsingle, false);
 
-  EXPECT_TRUE(PInf.bitwiseIsEqual(scalbn(PInf, 0)));
-  EXPECT_TRUE(MInf.bitwiseIsEqual(scalbn(MInf, 0)));
-  EXPECT_TRUE(PZero.bitwiseIsEqual(scalbn(PZero, 0)));
-  EXPECT_TRUE(MZero.bitwiseIsEqual(scalbn(MZero, 0)));
-  EXPECT_TRUE(QPNaN.bitwiseIsEqual(scalbn(QPNaN, 0)));
-  EXPECT_TRUE(QMNaN.bitwiseIsEqual(scalbn(QMNaN, 0)));
-  EXPECT_TRUE(SNaN.bitwiseIsEqual(scalbn(SNaN, 0)));
+  EXPECT_TRUE(PInf.bitwiseIsEqual(scalbn(PInf, 0, RM)));
+  EXPECT_TRUE(MInf.bitwiseIsEqual(scalbn(MInf, 0, RM)));
+  EXPECT_TRUE(PZero.bitwiseIsEqual(scalbn(PZero, 0, RM)));
+  EXPECT_TRUE(MZero.bitwiseIsEqual(scalbn(MZero, 0, RM)));
+  EXPECT_TRUE(QPNaN.bitwiseIsEqual(scalbn(QPNaN, 0, RM)));
+  EXPECT_TRUE(QMNaN.bitwiseIsEqual(scalbn(QMNaN, 0, RM)));
+  EXPECT_TRUE(SNaN.bitwiseIsEqual(scalbn(SNaN, 0, RM)));
 
-  EXPECT_TRUE(
-      PInf.bitwiseIsEqual(scalbn(APFloat(APFloat::IEEEsingle, "0x1p+0"), 128)));
+  EXPECT_TRUE(PInf.bitwiseIsEqual(
+                scalbn(APFloat(APFloat::IEEEsingle, "0x1p+0"), 128, RM)));
   EXPECT_TRUE(MInf.bitwiseIsEqual(
-      scalbn(APFloat(APFloat::IEEEsingle, "-0x1p+0"), 128)));
-  EXPECT_TRUE(
-      PInf.bitwiseIsEqual(scalbn(APFloat(APFloat::IEEEsingle, "0x1p+127"), 1)));
+                scalbn(APFloat(APFloat::IEEEsingle, "-0x1p+0"), 128, RM)));
+  EXPECT_TRUE(PInf.bitwiseIsEqual(
+                scalbn(APFloat(APFloat::IEEEsingle, "0x1p+127"), 1, RM)));
   EXPECT_TRUE(PZero.bitwiseIsEqual(
-      scalbn(APFloat(APFloat::IEEEsingle, "0x1p+0"), -127)));
+                scalbn(APFloat(APFloat::IEEEsingle, "0x1p-127"), -127, RM)));
   EXPECT_TRUE(MZero.bitwiseIsEqual(
-      scalbn(APFloat(APFloat::IEEEsingle, "-0x1p+0"), -127)));
+                scalbn(APFloat(APFloat::IEEEsingle, "-0x1p-127"), -127, RM)));
+  EXPECT_TRUE(APFloat(APFloat::IEEEsingle, "-0x1p-149").bitwiseIsEqual(
+                scalbn(APFloat(APFloat::IEEEsingle, "-0x1p-127"), -22, RM)));
   EXPECT_TRUE(PZero.bitwiseIsEqual(
-      scalbn(APFloat(APFloat::IEEEsingle, "0x1p-126"), -1)));
-  EXPECT_TRUE(PZero.bitwiseIsEqual(
-      scalbn(APFloat(APFloat::IEEEsingle, "0x1p-126"), -1)));
+                scalbn(APFloat(APFloat::IEEEsingle, "0x1p-126"), -24, RM)));
+
+
+  APFloat SmallestF64 = APFloat::getSmallest(APFloat::IEEEdouble, false);
+  APFloat NegSmallestF64 = APFloat::getSmallest(APFloat::IEEEdouble, true);
+
+  APFloat LargestF64 = APFloat::getLargest(APFloat::IEEEdouble, false);
+  APFloat NegLargestF64 = APFloat::getLargest(APFloat::IEEEdouble, true);
+
+  APFloat SmallestNormalizedF64
+    = APFloat::getSmallestNormalized(APFloat::IEEEdouble, false);
+  APFloat NegSmallestNormalizedF64
+    = APFloat::getSmallestNormalized(APFloat::IEEEdouble, true);
+
+  APFloat LargestDenormalF64(APFloat::IEEEdouble, "0x1.ffffffffffffep-1023");
+  APFloat NegLargestDenormalF64(APFloat::IEEEdouble, "-0x1.ffffffffffffep-1023");
+
+
+  EXPECT_TRUE(SmallestF64.bitwiseIsEqual(
+                scalbn(APFloat(APFloat::IEEEdouble, "0x1p-1074"), 0, RM)));
+  EXPECT_TRUE(NegSmallestF64.bitwiseIsEqual(
+                scalbn(APFloat(APFloat::IEEEdouble, "-0x1p-1074"), 0, RM)));
+
+  EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1p+1023")
+              .bitwiseIsEqual(scalbn(SmallestF64, 2097, RM)));
+
+  EXPECT_TRUE(scalbn(SmallestF64, -2097, RM).isPosZero());
+  EXPECT_TRUE(scalbn(SmallestF64, -2098, RM).isPosZero());
+  EXPECT_TRUE(scalbn(SmallestF64, -2099, RM).isPosZero());
+  EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1p+1022")
+              .bitwiseIsEqual(scalbn(SmallestF64, 2096, RM)));
+  EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1p+1023")
+              .bitwiseIsEqual(scalbn(SmallestF64, 2097, RM)));
+  EXPECT_TRUE(scalbn(SmallestF64, 2098, RM).isInfinity());
+  EXPECT_TRUE(scalbn(SmallestF64, 2099, RM).isInfinity());
+
+  // Test for integer overflows when adding to exponent.
+  EXPECT_TRUE(scalbn(SmallestF64, -INT_MAX, RM).isPosZero());
+  EXPECT_TRUE(scalbn(LargestF64, INT_MAX, RM).isInfinity());
+
+  EXPECT_TRUE(LargestDenormalF64
+              .bitwiseIsEqual(scalbn(LargestDenormalF64, 0, RM)));
+  EXPECT_TRUE(NegLargestDenormalF64
+              .bitwiseIsEqual(scalbn(NegLargestDenormalF64, 0, RM)));
+
+  EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1.ffffffffffffep-1022")
+              .bitwiseIsEqual(scalbn(LargestDenormalF64, 1, RM)));
+  EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "-0x1.ffffffffffffep-1021")
+              .bitwiseIsEqual(scalbn(NegLargestDenormalF64, 2, RM)));
+
+  EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1.ffffffffffffep+1")
+              .bitwiseIsEqual(scalbn(LargestDenormalF64, 1024, RM)));
+  EXPECT_TRUE(scalbn(LargestDenormalF64, -1023, RM).isPosZero());
+  EXPECT_TRUE(scalbn(LargestDenormalF64, -1024, RM).isPosZero());
+  EXPECT_TRUE(scalbn(LargestDenormalF64, -2048, RM).isPosZero());
+  EXPECT_TRUE(scalbn(LargestDenormalF64, 2047, RM).isInfinity());
+  EXPECT_TRUE(scalbn(LargestDenormalF64, 2098, RM).isInfinity());
+  EXPECT_TRUE(scalbn(LargestDenormalF64, 2099, RM).isInfinity());
+
+  EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1.ffffffffffffep-2")
+              .bitwiseIsEqual(scalbn(LargestDenormalF64, 1021, RM)));
+  EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1.ffffffffffffep-1")
+              .bitwiseIsEqual(scalbn(LargestDenormalF64, 1022, RM)));
+  EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1.ffffffffffffep+0")
+              .bitwiseIsEqual(scalbn(LargestDenormalF64, 1023, RM)));
+  EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1.ffffffffffffep+1023")
+              .bitwiseIsEqual(scalbn(LargestDenormalF64, 2046, RM)));
+  EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1p+974")
+              .bitwiseIsEqual(scalbn(SmallestF64, 2048, RM)));
+
+  APFloat RandomDenormalF64(APFloat::IEEEdouble, "0x1.c60f120d9f87cp+51");
+  EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1.c60f120d9f87cp-972")
+              .bitwiseIsEqual(scalbn(RandomDenormalF64, -1023, RM)));
+  EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1.c60f120d9f87cp-1")
+              .bitwiseIsEqual(scalbn(RandomDenormalF64, -52, RM)));
+  EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1.c60f120d9f87cp-2")
+              .bitwiseIsEqual(scalbn(RandomDenormalF64, -53, RM)));
+  EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1.c60f120d9f87cp+0")
+              .bitwiseIsEqual(scalbn(RandomDenormalF64, -51, RM)));
+
+  EXPECT_TRUE(scalbn(RandomDenormalF64, -2097, RM).isPosZero());
+  EXPECT_TRUE(scalbn(RandomDenormalF64, -2090, RM).isPosZero());
+
+
+  EXPECT_TRUE(
+    APFloat(APFloat::IEEEdouble, "-0x1p-1073")
+    .bitwiseIsEqual(scalbn(NegLargestF64, -2097, RM)));
+
+  EXPECT_TRUE(
+    APFloat(APFloat::IEEEdouble, "-0x1p-1024")
+    .bitwiseIsEqual(scalbn(NegLargestF64, -2048, RM)));
+
+  EXPECT_TRUE(
+    APFloat(APFloat::IEEEdouble, "0x1p-1073")
+    .bitwiseIsEqual(scalbn(LargestF64, -2097, RM)));
+
+  EXPECT_TRUE(
+    APFloat(APFloat::IEEEdouble, "0x1p-1074")
+    .bitwiseIsEqual(scalbn(LargestF64, -2098, RM)));
+  EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "-0x1p-1074")
+              .bitwiseIsEqual(scalbn(NegLargestF64, -2098, RM)));
+  EXPECT_TRUE(scalbn(NegLargestF64, -2099, RM).isNegZero());
+  EXPECT_TRUE(scalbn(LargestF64, 1, RM).isInfinity());
+
+
+  EXPECT_TRUE(
+    APFloat(APFloat::IEEEdouble, "0x1p+0")
+    .bitwiseIsEqual(scalbn(APFloat(APFloat::IEEEdouble, "0x1p+52"), -52, RM)));
+
+  EXPECT_TRUE(
+    APFloat(APFloat::IEEEdouble, "0x1p-103")
+    .bitwiseIsEqual(scalbn(APFloat(APFloat::IEEEdouble, "0x1p-51"), -52, RM)));
 }
 }




More information about the llvm-commits mailing list