[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);