lmi-commits
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[lmi-commits] [lmi] master f0457dc 1/2: Reimplement scale_power()


From: Greg Chicares
Subject: [lmi-commits] [lmi] master f0457dc 1/2: Reimplement scale_power()
Date: Wed, 21 Mar 2018 19:30:20 -0400 (EDT)

branch: master
commit f0457dcd56f43f1a952d1d4f4ac6610bb7b0c02b
Author: Gregory W. Chicares <address@hidden>
Commit: Gregory W. Chicares <address@hidden>

    Reimplement scale_power()
    
    Determine the formatted width of numeric column vectors (represented
    by their minimum and maximum elements) by calling std::snprintf().
    
    This is more robust than the std::log10() algorithm it replaces. In
    this example:
      999,999,999.49 (log 8.999999999779) -->   999,999,999  9 digits
      999,999,999.50 (log 8.999999999783) --> 1,000,000,000 10 digits
    it distinguishes small integers instead of comparing logarithms that
    agree to within a relative error of 5e-13 (the example is germane even
    though scale_power() rounds to whole integers). It also handles "-0"
    and "inf" correctly.
    
    std::snprintf() is much costlier than std::log10(), but the cost has
    been mitigated by recent changes that first determine the minimum and
    maximum of all scalable columns in a ledger. This cost is negligible in
    the context where scale_power() is used, which formats and prints every
    value for an illustration.
---
 miscellany.cpp | 26 +++++++++++++++-----------
 1 file changed, 15 insertions(+), 11 deletions(-)

diff --git a/miscellany.cpp b/miscellany.cpp
index 665c234..9513cff 100644
--- a/miscellany.cpp
+++ b/miscellany.cpp
@@ -25,10 +25,10 @@
 
 #include "alert.hpp"
 #include "assert_lmi.hpp"
-#include "stl_extensions.hpp"           // nonstd::power()
 
 #include <algorithm>                    // equal(), max()
-#include <cmath>                        // ceil(), floor(), log10()
+#include <cmath>                        // ceil(), floor()
+#include <cstdio>                       // snprintf()
 #include <ctime>
 #include <fstream>
 #include <istream>
@@ -124,22 +124,26 @@ int scale_power(int max_power, double min_value, double 
max_value)
     LMI_ASSERT(3 <= max_power);
     LMI_ASSERT(min_value <= max_value);
 
-    // Round to int, away from zero, and multiply by ten if negative:
-    // a negative value needs an extra '-' character: i.e., as many
-    // total characters as ten times its absolute value requires.
-    auto adjust = [](double d)
-        {return (d < 0) ? -10.0 * std::floor(d) : std::ceil(d);};
+    // Round to int, away from zero.
+    auto round_outward = [](double d)
+        {return (d < 0) ? std::floor(d) : std::ceil(d);};
 
-    double widest = std::max(adjust(min_value), adjust(max_value));
+    // One value; two names; two meanings.
+    //  extremum < 10^max_power <-> formatted width <= chars_available
+    // for nonnegative extrema (and negatives are handled correctly).
+    int const chars_available = max_power;
+
+    int const chars_required = std::max
+        (std::snprintf(nullptr, 0, "%.f", round_outward(min_value))
+        ,std::snprintf(nullptr, 0, "%.f", round_outward(max_value))
+        );
 
-    if(0 == widest || widest < nonstd::power(10.0, max_power))
+    if(chars_required <= chars_available)
         {
         return 0;
         }
 
     // Only characters [0-9-] to the left of any decimal point matter.
-    int const chars_required  = 1 + static_cast<int>(std::log10(widest));
-    int const chars_available = max_power;
     int const excess = chars_required - chars_available;
     LMI_ASSERT(0 < excess);
     int const r = 3 * (1 + (excess - 1) / 3);



reply via email to

[Prev in Thread] Current Thread [Next in Thread]