[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[lmi-commits] [lmi] master 5e90841 2/4: Traffick in ints sooner than in
From: |
Greg Chicares |
Subject: |
[lmi-commits] [lmi] master 5e90841 2/4: Traffick in ints sooner than in floats |
Date: |
Thu, 15 Mar 2018 16:34:41 -0400 (EDT) |
branch: master
commit 5e9084159a7bffd3adbcba7133c58a264939d59f
Author: Gregory W. Chicares <address@hidden>
Commit: Gregory W. Chicares <address@hidden>
Traffick in ints sooner than in floats
Where numeric values are to be scaled by 10^-6 for printing, store the
scaling parameter as an integer like 6, not as a decimal like 0.000001,
because the integer might be wanted later. Here, 6 is preferred to -6
because the negation is implicit in the algorithm, but the |6| is data.
int(6) can always be converted reliably to 10^6, and 10.0^6 is reliably
integer-valued if nonstd::power() is used instead of a possibly flawed
std::pow(). OTOH, 10.0^-6 can have no exact binary representation, and
relying on even an optimal standard library to recover the exponent as
an exact integer will someday end in tears--immediately in the happiest
circumstance, or decades later, as in the present case.
Subtleties:
- To determine the proper scaling for a set of columns, std::min()
was applied to 0.001 and 0.000001, where std::max() must be applied
to 3 and 6, in order for uniform scaling to make them all fit.
- There is no need to assert that the multiplier is nonzero, because
no value of the integral exponent will make it so.
- The default scaling parameter, formerly a multiplier of 1.0, becomes
an exponent of 0, of course.
---
ledger.cpp | 8 +++---
ledger_base.cpp | 83 +++++++++++++++++++++++----------------------------------
ledger_base.hpp | 11 ++++----
ledger_pdf.cpp | 2 +-
4 files changed, 44 insertions(+), 60 deletions(-)
diff --git a/ledger.cpp b/ledger.cpp
index a077b0b..c49f38d 100644
--- a/ledger.cpp
+++ b/ledger.cpp
@@ -315,19 +315,19 @@ int Ledger::GetMaxLength() const
// largest absolute value of any number in any column of every subledger.
void Ledger::AutoScale()
{
- double mult = ledger_invariant_->DetermineScaleFactor();
+ int k = ledger_invariant_->DetermineScalePower();
ledger_map_t& l_map_rep = ledger_map_->held_;
for(auto const& i : l_map_rep)
{
- mult = std::min(mult, i.second.DetermineScaleFactor());
+ k = std::max(k, i.second.DetermineScalePower());
}
- ledger_invariant_->ApplyScaleFactor(mult);
+ ledger_invariant_->ApplyScaleFactor(k);
for(auto& i : l_map_rep)
{
- i.second.ApplyScaleFactor(mult);
+ i.second.ApplyScaleFactor(k);
}
}
diff --git a/ledger_base.cpp b/ledger_base.cpp
index 205413a..46ac4fe 100644
--- a/ledger_base.cpp
+++ b/ledger_base.cpp
@@ -27,24 +27,25 @@
#include "assert_lmi.hpp"
#include "crc32.hpp"
#include "miscellany.hpp" // minmax
+#include "stl_extensions.hpp" // nonstd::power()
#include "value_cast.hpp"
#include <algorithm> // max(), min(), transform()
-#include <cmath> // floor(), log10(), pow()
+#include <cmath> // floor(), log10()
#include <functional> // multiplies
//============================================================================
LedgerBase::LedgerBase(int a_Length)
- :scale_factor_(1.0)
- ,scale_unit_ ("")
+ :scale_power_(0)
+ ,scale_unit_ ("")
{
Initialize(a_Length);
}
//============================================================================
LedgerBase::LedgerBase(LedgerBase const& obj)
- :scale_factor_(obj.scale_factor_)
- ,scale_unit_ (obj.scale_unit_)
+ :scale_power_(obj.scale_power_)
+ ,scale_unit_ (obj.scale_unit_)
{
Initialize(obj.GetLength());
Copy(obj);
@@ -55,8 +56,8 @@ LedgerBase& LedgerBase::operator=(LedgerBase const& obj)
{
if(this != &obj)
{
- scale_factor_ = obj.scale_factor_;
- scale_unit_ = obj.scale_unit_;
+ scale_power_ = obj.scale_power_;
+ scale_unit_ = obj.scale_unit_;
Initialize(obj.GetLength());
Copy(obj);
}
@@ -103,7 +104,7 @@ void LedgerBase::Copy(LedgerBase const& obj)
// Rather, their contents are information that is added in by derived
// classes.
//
- // scale_factor_ and scale_unit_ aren't copied here because they're
+ // scale_power_ and scale_unit_ aren't copied here because they're
// copied explicitly by the caller.
//
// TODO ?? There has to be a way to abstract this.
@@ -228,8 +229,7 @@ LedgerBase& LedgerBase::PlusEq
,std::vector<double> const& a_Inforce
)
{
- LMI_ASSERT(0.0 != scale_factor_);
- if(scale_factor_ != a_Addend.scale_factor_)
+ if(scale_power_ != a_Addend.scale_power_)
{
alarum() << "Cannot add differently scaled ledgers." << LMI_FLUSH;
}
@@ -313,7 +313,7 @@ LedgerBase& LedgerBase::PlusEq
// and make them variables.
// PDF !! This seems not to be rigorously correct: $999,999,999.99 is
// less than one billion, but rounds to $1,000,000,000.
-double LedgerBase::DetermineScaleFactor() const
+int LedgerBase::DetermineScalePower() const
{
double min_val = 0.0;
double max_val = 0.0;
@@ -335,44 +335,27 @@ double LedgerBase::DetermineScaleFactor() const
if(widest < 1000000000.0 || widest == 0)
{
- return 1.0;
+ return 0;
}
+
double d = std::log10(widest);
- d = 3.0 * std::floor(d / 3.0);
- d = std::pow(10.0, 6.0 - d);
+ d = std::floor(d / 3.0);
+ int k = 3 * static_cast<int>(d);
+ k = k - 6;
- LMI_ASSERT(1.0E-18 <= d);
- LMI_ASSERT(d <= 1.0);
+ LMI_ASSERT(0 <= k);
+ LMI_ASSERT(k <= 18);
- return d;
+ return k;
}
namespace
{
- static std::string look_up_scale_unit(double a_ScalingFactor)
+ static std::string look_up_scale_unit(int decimal_power)
{
- if(0.0 == a_ScalingFactor)
- {
- alarum() << "Scaling factor is zero." << LMI_FLUSH;
- return "ZERO";
- }
-
- double power = -std::log10(a_ScalingFactor);
- // Assert absolute equality of two floating-point quantities, because
- // they must both have integral values.
- // PDF !! Suppress the assertion because, with MinGW-w64 gcc-7.2.0,
- // it fails--apparently floor() gives the wrong answer, but trunc()
- // and static_cast<int>() give the right answer for the test case
- // described in the git commit message for 1c1bafa40. Obviously this
- // needs further work because the behavior in other cases is unknown,
- // and adding a small quantity to a near-integer to force it to round
- // "correctly" is surely bogus.
- // LMI_ASSERT(power == std::floor(power));
- int z = static_cast<int>(0.1 + power);
-
// US names are used; UK names are different.
// Assume that numbers over 999 quintillion (US) will not be needed.
- switch(z)
+ switch(decimal_power)
{
case 0:
{
@@ -411,7 +394,7 @@ namespace
// break;
default:
{
- alarum() << "Case '" << z << "' not found." << LMI_FLUSH;
+ alarum() << "Case '" << decimal_power << "' not found." <<
LMI_FLUSH;
throw "Unreachable--silences a compiler diagnostic.";
}
}
@@ -419,28 +402,27 @@ namespace
} // Unnamed namespace.
//============================================================================
-// Multiplies all scalable vectors by the factor from DetermineScaleFactor().
+// Multiplies all scalable vectors by the factor from DetermineScalePower().
// Only columns are scaled, so we operate here only on vectors. A header
// that shows e.g. face amount should show the true face amount, unscaled.
-void LedgerBase::ApplyScaleFactor(double a_Mult)
+void LedgerBase::ApplyScaleFactor(int decimal_power)
{
- LMI_ASSERT(0.0 != a_Mult);
- LMI_ASSERT(0.0 != scale_factor_);
- if(1.0 != scale_factor_)
+ if(0 != scale_power_)
{
alarum() << "Cannot scale the same ledger twice." << LMI_FLUSH;
}
- scale_factor_ = a_Mult;
- if(1.0 == scale_factor_)
+ scale_power_ = decimal_power;
+ if(0 == scale_power_)
{
// Don't waste time multiplying all these vectors by one
return;
}
- scale_unit_ = look_up_scale_unit(scale_factor_);
+
+ scale_unit_ = look_up_scale_unit(scale_power_);
// ET !! *i.second *= M;
- std::vector<double>M(GetLength(), scale_factor_);
+ std::vector<double>M(GetLength(), 1.0 / nonstd::power(10.0, scale_power_));
for(auto& i : ScalableVectors)
{
std::vector<double>& v = *i.second;
@@ -461,9 +443,10 @@ std::string const& LedgerBase::ScaleUnit() const
}
//============================================================================
-double LedgerBase::ScaleFactor() const
+// PDF !! expunge
+int LedgerBase::ScalePower() const
{
- return scale_factor_;
+ return scale_power_;
}
//============================================================================
diff --git a/ledger_base.hpp b/ledger_base.hpp
index 70944f5..0c3902a 100644
--- a/ledger_base.hpp
+++ b/ledger_base.hpp
@@ -174,11 +174,12 @@ class LMI_SO LedgerBase
public:
virtual ~LedgerBase() = default;
- void ApplyScaleFactor(double a_Mult);
+ void ApplyScaleFactor(int decimal_power);
- double DetermineScaleFactor() const;
+ int DetermineScalePower() const;
std::string const& ScaleUnit() const;
- double ScaleFactor() const;
+// PDF !! expunge
+ int ScalePower() const;
std::string value_str(std::string const& map_key, int index) const;
std::string value_str(std::string const& map_key) const;
@@ -230,8 +231,8 @@ class LMI_SO LedgerBase
string_map Strings;
private:
- double scale_factor_;
- std::string scale_unit_; // E.g. "thousands", "millions".
+ int scale_power_; // E.g., for (000,000): 6
+ std::string scale_unit_; // E.g., for (000,000): "millions"
};
template<typename T> void SpewVector
diff --git a/ledger_pdf.cpp b/ledger_pdf.cpp
index 850ac3b..d3eb99c 100644
--- a/ledger_pdf.cpp
+++ b/ledger_pdf.cpp
@@ -91,7 +91,7 @@ std::string write_ledger_as_pdf(Ledger const& ledger,
fs::path const& filepath)
Ledger scaled_ledger(ledger);
// PDF !! Expunge this 'if' line (but not the statement it controls):
- if(1.0 == scaled_ledger.GetLedgerInvariant().ScaleFactor())
+ if(0 == scaled_ledger.GetLedgerInvariant().ScalePower())
scaled_ledger.AutoScale();
auto const pdf = ledger_pdf_generator::create();
pdf->write(scaled_ledger, pdf_out_file);