瀏覽代碼

Add support for min, max, inf and NaN in ctkUtils

Closes #348
Julien Finet 11 年之前
父節點
當前提交
22326a9459
共有 3 個文件被更改,包括 95 次插入32 次删除
  1. 65 22
      Libs/Core/Testing/Cpp/ctkUtilsTest.cpp
  2. 27 8
      Libs/Core/ctkUtils.cpp
  3. 3 2
      Libs/Core/ctkUtils.h

+ 65 - 22
Libs/Core/Testing/Cpp/ctkUtilsTest.cpp

@@ -78,6 +78,12 @@ void ctkUtilsTester::testOrderOfMagnitude_data()
   QTest::newRow("10.0001 -> 1") << 10.0001 << 1;
   QTest::newRow("100000000001.0001 -> 11") << 100000000001.0001 << 11;
   QTest::newRow("0. -> min") << 0. << std::numeric_limits<int>::min();
+  QTest::newRow("inf -> min") << std::numeric_limits<double>::infinity() << std::numeric_limits<int>::min();
+  QTest::newRow("-inf -> min") << -std::numeric_limits<double>::infinity() << std::numeric_limits<int>::min();
+  QTest::newRow("nan -> min") << std::numeric_limits<double>::quiet_NaN()  << std::numeric_limits<int>::min();
+  QTest::newRow("min -> min") << std::numeric_limits<double>::min() << std::numeric_limits<int>::min();
+  QTest::newRow("max -> 308") << std::numeric_limits<double>::max() << 308;
+  QTest::newRow("denorm -> min") << std::numeric_limits<double>::denorm_min() << std::numeric_limits<int>::min();
 }
 
 // ----------------------------------------------------------------------------
@@ -85,17 +91,24 @@ void ctkUtilsTester::testClosestPowerOfTen()
 {
   QFETCH(double, value);
   QFETCH(double, expectedValue);
-  QFETCH(bool, compareWithEpsilon);
+  QFETCH(int, compareMode);
   const double closestValue = ctk::closestPowerOfTen(value);
-  if (compareWithEpsilon)
+  switch (compareMode)
     {
-    const double epsilon = std::numeric_limits<double>::epsilon();
-    QVERIFY( closestValue > expectedValue - epsilon );
-    QVERIFY( closestValue < expectedValue + epsilon );
-    }
-  else
-    {
-    QCOMPARE(closestValue, expectedValue);
+    default:
+    case 0:
+      QVERIFY(closestValue == expectedValue);
+      break;
+    case 1:
+      {
+      const double epsilon = std::numeric_limits<double>::epsilon();
+      QVERIFY( closestValue > expectedValue - epsilon );
+      QVERIFY( closestValue < expectedValue + epsilon );
+      break;
+      }
+    case 2:
+      QVERIFY( closestValue != closestValue );
+      break;
     }
 }
 
@@ -104,20 +117,41 @@ void ctkUtilsTester::testClosestPowerOfTen_data()
 {
   QTest::addColumn<double>("value");
   QTest::addColumn<double>("expectedValue");
-  QTest::addColumn<bool>("compareWithEpsilon");
-
-  QTest::newRow("1. -> 1.") << 1. << 1. << false;
-  QTest::newRow("2. -> 1.") << 2. << 1. << false;
-  QTest::newRow("10. -> 10.") << 10. << 10. << false;
-  QTest::newRow("45. -> 10.") << 45. << 10. << false;
-  QTest::newRow("98. -> 100.") << 98. << 100. << false;
-  QTest::newRow("50. -> 10.") << 50. << 10. << false;
-  QTest::newRow("-1234. -> -1000.") << -1234. << -1000. << false;
-  QTest::newRow("0.01 -> 0.01") << 0.01 << 0.01 << true;
+  /// 0 exact compare
+  /// 1 compare with epsilon
+  /// 2 isNaN
+  QTest::addColumn<int>("compareMode");
+
+  QTest::newRow("1. -> 1.") << 1. << 1. << 0;
+  QTest::newRow("2. -> 1.") << 2. << 1. << 0;
+  QTest::newRow("10. -> 10.") << 10. << 10. << 0;
+  QTest::newRow("45. -> 10.") << 45. << 10. << 0;
+  QTest::newRow("98. -> 100.") << 98. << 100. << 0;
+  QTest::newRow("50. -> 10.") << 50. << 10. << 0;
+  QTest::newRow("-1234. -> -1000.") << -1234. << -1000. << 0;
+  QTest::newRow("0.01 -> 0.01") << 0.01 << 0.01 << 1;
   QTest::newRow("0.00000000015 -> 0.0000000001")
-    << 0.00000000015 << 0.0000000001 << true;
-  QTest::newRow("0.1 -> 0.1") << 0.1 << 0.1 << true;
-  QTest::newRow("0. -> 0.") << 0. << 0. << false;
+    << 0.00000000015 << 0.0000000001 << 1;
+  QTest::newRow("0.1 -> 0.1") << 0.1 << 0.1 << 1;
+  QTest::newRow("0. -> 0.") << 0. << 0. << 0;
+  QTest::newRow("inf -> inf") << std::numeric_limits<double>::infinity()
+                              << std::numeric_limits<double>::infinity()
+                              << 0;
+  QTest::newRow("-inf -> -inf") << -std::numeric_limits<double>::infinity()
+                                << -std::numeric_limits<double>::infinity()
+                                << 0;
+  QTest::newRow("nan -> nan") << std::numeric_limits<double>::quiet_NaN()
+                              << std::numeric_limits<double>::quiet_NaN()
+                              << 2;
+  QTest::newRow("min -> min") << std::numeric_limits<double>::min()
+                              << std::numeric_limits<double>::min()
+                              << 0;
+  //QTest::newRow("max -> max") << std::numeric_limits<double>::max()
+  //                            << 1e+308
+  //                            << 0;
+  QTest::newRow("denorm -> denorm") << std::numeric_limits<double>::denorm_min()
+                                    << std::numeric_limits<double>::denorm_min()
+                                    << 0;
 }
 
 // ----------------------------------------------------------------------------
@@ -163,6 +197,15 @@ void ctkUtilsTester::testSignificantDecimals_data()
   QTest::newRow("0.25 -> 2") << 0.25 << 2;
   QTest::newRow("0.125 -> 3") << 0.125 << 3;
   QTest::newRow("0.1234567891013151 -> 16") << 0.1234567891013151 << 16;
+  QTest::newRow("0. -> 0") << 0. << 0;
+  QTest::newRow("inf -> 0") << std::numeric_limits<double>::infinity() << 0;
+  QTest::newRow("-inf -> 0") << -std::numeric_limits<double>::infinity() << 0;
+  QTest::newRow("nan -> -1") << std::numeric_limits<double>::quiet_NaN() << -1;
+  QTest::newRow("min -> 16") << std::numeric_limits<double>::min() << 16;
+  QTest::newRow("max -> 0") << std::numeric_limits<double>::max() << 0;
+  QTest::newRow("denorm -> 16") << std::numeric_limits<double>::denorm_min()
+                                << 16;
+
 }
 
 // ----------------------------------------------------------------------------

+ 27 - 8
Libs/Core/ctkUtils.cpp

@@ -164,12 +164,22 @@ QRegExp ctk::nameFiltersToRegExp(const QStringList& nameFilters)
 //-----------------------------------------------------------------------------
 int ctk::significantDecimals(double value)
 {
+  if (value == 0.
+      || qAbs(value) == std::numeric_limits<double>::infinity())
+    {
+    return 0;
+    }
+  if (value != value) // is NaN
+    {
+    return -1;
+    }
   QString number = QString::number(value, 'f', 16);
   QString fractional = number.section('.', 1, 1);
   Q_ASSERT(fractional.length() == 16);
   QChar previous;
   int previousRepeat=0;
   bool only0s = true;
+  bool isUnit = value > -1. && value < 1.;
   for (int i = 0; i < fractional.length(); ++i)
     {
     QChar digit = fractional.at(i);
@@ -190,7 +200,7 @@ int ctk::significantDecimals(double value)
     // Last digit
     if (i == fractional.length() - 1)
       {
-      if (previousRepeat > 2)
+      if (previousRepeat > 2 && !(only0s && isUnit) )
         {
         return i - previousRepeat;
         }
@@ -215,7 +225,11 @@ int ctk::significantDecimals(double value)
 int ctk::orderOfMagnitude(double value)
 {
   value = qAbs(value);
-  if (value == 0.)
+  if (value == 0.
+      || value == std::numeric_limits<double>::infinity()
+      || value != value // is NaN
+      || value < std::numeric_limits<double>::epsilon() // is tool small to compute
+  )
     {
     return std::numeric_limits<int>::min();
     }
@@ -232,8 +246,9 @@ int ctk::orderOfMagnitude(double value)
     magnitudeFactor = 0.1;
     }
 
+  double epsilon = std::numeric_limits<double>::epsilon();
   while ( (magnitudeStep > 0 && value >= magnitude) ||
-          (magnitudeStep < 0 && value < magnitude - std::numeric_limits<double>::epsilon()))
+          (magnitudeStep < 0 && value < magnitude - epsilon))
     {
     magnitude *= magnitudeFactor;
     magnitudeOrder += magnitudeStep;
@@ -243,13 +258,17 @@ int ctk::orderOfMagnitude(double value)
 }
 
 //-----------------------------------------------------------------------------
-double ctk::closestPowerOfTen(double value)
+double ctk::closestPowerOfTen(double _value)
 {
-  double sign = value >= 0. ? 1 : -1;
-  value = qAbs(value);
-  if (value == 0.)
+  const double sign = _value >= 0. ? 1 : -1;
+  const double value = qAbs(_value);
+  if (value == 0.
+      || value == std::numeric_limits<double>::infinity()
+      || value != value // is NaN
+      || value < std::numeric_limits<double>::epsilon() // is denormalized
+  )
     {
-    return 0.;
+    return _value;
     }
 
   double magnitude = 1.;

+ 3 - 2
Libs/Core/ctkUtils.h

@@ -78,7 +78,7 @@ QRegExp CTK_CORE_EXPORT nameFiltersToRegExp(const QStringList& nameFilters);
 ///
 /// \ingroup Core
 /// Return a "smart" number of decimals needed to display (in a gui) a floating
-/// number.
+/// number. 16 is the max that can be returned, -1 for NaN numbers.
 /// e.g. significantDecimals(120.01) returns 2
 ///      significantDecimals(123456.1333333) returns 3
 ///      significantDecimals(123456.26999999999999996) returns 2
@@ -87,7 +87,8 @@ int CTK_CORE_EXPORT significantDecimals(double value);
 
 ///
 /// \ingroup Core
-/// Return the order of magnitude of a number.
+/// Return the order of magnitude of a number or numeric_limits<int>::min() if
+/// the order of magnitude can't be computed (e.g. 0, inf, Nan, denorm)...
 /// e.g.: orderOfMagnitude(1) returns 0
 ///       orderOfMagnitude(10) returns 1
 ///       orderOfMagnitude(99) returns 1