Compare commits

..

5 commits

Author SHA1 Message Date
b11abb2d15 Parameters: resolve type precision problems with scale-joinig
The selection of the intermediary working type, used for exponential
and logarithmic functions when combining values from different scales,
was implemented too simplistic.

Following the example of the `NumberJoinType`, it seems preferrable
to spell out the decision logic explicitly. This allows to pick `long doubl`
as working type especially in cases where 64bit integral parameters are
involved, either as source or destination.

With these final tweaks, the `verify_valueJoining()` test is not complete,
presumably with path coverage of the conversion functions.

The Scale descriptor and operational logic is now defined to the point
that it can be integrated into the general Parameter implementation machinery.
2026-09-22 18:39:09 +02:00
698e789eb6 Parameters: verify use of a long-double target scale
Scrutinising the bounds of numeric precision.
As it turns out, most cases work as expected,
including the massive errors incurred by input-values given as float.

However: a logarithmic combination integral -> integral runs in loss of precision
So the selection logic for the intermediary working type is still overly simplistic!
2026-09-22 01:43:45 +02:00
346d663d51 Parameters: expand logarithmic scale patch coverage
The control logic for the various combinations of linear and logarithmic scales is nasty,
and the added path coverage indeed unveiled several bugs.

One of these issues, an unjustified limitation on the input value type of `asLogarithm`,
convinced me to refactor the conversion functions into free functions,
since they effectively ''always span the relation of two scales'' --
writing them as member function introduces an unnecessary asymetry.
2026-09-21 22:10:18 +02:00
ef60681b1d Parameters: cover logarithmic scale conversions
this feature relies on the common logarithmic math,
yet with the additional twist that decibels need an additional factor of 10...
2026-09-21 05:42:33 +02:00
7d141c52a6 Library: better short-hand name for long double
As with all those C++ base types, unfortunately they are giving you
only rough size guarantees. For Lumiera, I defined short hand names
some time ago, notably because the explicit long X and short X qualifiers
do not work with ''constructor syntax'' for direct conversions.

But I choose the name `f128` at that time -- which might raise ill-guided
expectations, since the `long double` is not that long on relevant linux 64bit
platforms; it can just hold 64bit of mantissa at least however.

As replacement, I'll use `lflp` now, because it has also 4 letters,
looks somewhat weird (and thus catches attention) and points into the
right direction. I considered `lfloat` as alternative, but that looks
too much like `float`, visually...
2026-09-21 01:05:53 +02:00
14 changed files with 552 additions and 185 deletions

View file

@ -363,11 +363,11 @@ namespace util {
string showDecimal (double val) noexcept { return showFloatingPoint (val, PRECISION_DECIMAL<double>); }
string showDecimal (float val) noexcept { return showFloatingPoint (val, PRECISION_DECIMAL<float>); }
string showDecimal (lflo val) noexcept { return showFloatingPoint (val, PRECISION_DECIMAL<lflo>); }
string showDecimal (lflp val) noexcept { return showFloatingPoint (val, PRECISION_DECIMAL<lflp>); }
string showComplete (double val) noexcept { return showFloatingPoint (val, PRECISION_COMPLETE<double>); }
string showComplete (float val) noexcept { return showFloatingPoint (val, PRECISION_COMPLETE<float>); }
string showComplete (lflo val) noexcept { return showFloatingPoint (val, PRECISION_COMPLETE<lflo>); }
string showComplete (lflp val) noexcept { return showFloatingPoint (val, PRECISION_COMPLETE<lflp>); }
string

View file

@ -93,10 +93,10 @@ namespace util {
std::string showDecimal (double) noexcept;
std::string showDecimal (float) noexcept;
std::string showDecimal (lflo) noexcept;
std::string showDecimal (lflp) noexcept;
std::string showComplete (double)noexcept;
std::string showComplete (float) noexcept;
std::string showComplete (lflo) noexcept;
std::string showComplete (lflp) noexcept;

View file

@ -32,8 +32,8 @@ using llong = long long int;
using ullong = unsigned long long int;
using ushort = unsigned short int;
using lflo = long double;
static_assert(10 <= sizeof(lflo));
using lflp = long double;
static_assert(10 <= sizeof(lflp));
const uint LIFE_AND_UNIVERSE_4EVER = 42;

View file

@ -456,12 +456,12 @@ namespace util {
/** show maximum reproducible decimal representation */
std::string showDecimal (double) noexcept;
std::string showDecimal (float) noexcept;
std::string showDecimal (lflo) noexcept;
std::string showDecimal (lflp) noexcept;
/** show enough decimal digits to represent every distinct value */
std::string showComplete (double) noexcept;
std::string showComplete (float) noexcept;
std::string showComplete (lflo) noexcept;
std::string showComplete (lflp) noexcept;
std::string showSize (size_t) noexcept;

View file

@ -42,6 +42,7 @@ namespace lib {
namespace par {
namespace err = lumiera::error;
using std::abs;
using util::_Fmt;
using std::optional;
@ -79,6 +80,7 @@ namespace par {
bool isValid() const;
bool isFactor() const;
bool isCyclic() const;
bool isLimited() const;
bool isLogarithmic() const;
/* ===== conforming operations ===== */
@ -90,16 +92,6 @@ namespace par {
VAL join (VAL const&, SRC const&, Scale<SRC> const&) const;
VAL join (VAL const&, VAL const&) const;
/* ===== conversion helpers ===== */
template<typename TAR>
auto asFactor (VAL const&) const;
template<typename TAR>
auto baseScale (Metric targetMetric) const;
template<typename TAR>
auto asLogarithm (Metric targetMetric, VAL const&) const;
};
@ -135,7 +127,8 @@ namespace par {
and (not isCyclic()
or (minVal and maxVal
and *minVal < *maxVal
and VAL(0) < cyclicLim)
and VAL(0) < cyclicLim
and abs (*maxVal) <= cyclicLim)
)
;
}
@ -156,6 +149,14 @@ namespace par {
return VAL(0) != cyclicLim;
}
template<typename VAL>
inline bool
Scale<VAL>::isLimited() const
{
return (maxVal or minVal)
and not isCyclic();
}
template<typename VAL>
inline bool
Scale<VAL>::isLogarithmic() const
@ -191,7 +192,7 @@ namespace par {
{
REQUIRE (minVal and maxVal);
REQUIRE (*minVal < *maxVal);
if (std::is_floating_point_v<VAL>)
if constexpr (std::is_floating_point_v<VAL>)
if (not (std::abs (rawVal) < cyclicLim))
throw err::Invalid {_Fmt{"Parameter value %4.2g beyond supported numeric precision "
"for cyclic wrapping into [%1.2g...%1.2g[ "}
@ -208,6 +209,80 @@ namespace par {
}
namespace { // logarithmic scale conversions...
/** transform a logarithmic value into an exponential factor */
template<typename TAR, typename VAL>
inline auto
expFactor (Metric metric, VAL const& value)
{
using WorkType = CommonComputeType<VAL,TAR>;
switch (metric)
{
case NAT: return std::exp (WorkType(value));
case BIN: return std::exp2 (WorkType(value));
case DEC: return std::pow (WorkType(10), WorkType(value/10.0));
default:
throw err::Logic{_Fmt{"Attempt to compute exponential "
"from a non-logarithmic value with metric %d"}
% metric};
break;
}
}
/** transform this value into a suitable logarithm for the target scale */
template<typename TAR, typename VAL>
inline auto
asLogarithm (Metric targetMetric, VAL const& value)
{
if (isNeg (value))
throw err::Invalid{_Fmt{"Attempt to take logarithm from %f < 0"}
% value};
using WorkType = CommonComputeType<VAL,TAR>;
switch (targetMetric)
{
case NAT: return std::log (WorkType(value));
case BIN: return std::log2 (WorkType(value));
case DEC: return std::log10 (WorkType(value)) * 10;
default:
throw err::Logic{_Fmt{"Attempt to compute logarithm "
"for a non-logarithmic metric %d"}
% targetMetric};
break;
}
}
/** provide the adaptation factor from this logarithmic scale
* to the given other logarithmic scale and target type \a VAL
* @remarks
* - use the logarithm conversion formula logb(x) ≡ logb(k)·logk(x)
* - if target is in decibel, have to apply factor 10; asLogarithm() does that already
* - if we are in decibel, log10 = dB /10; fold that factor in the logarithm of the base,
* by using log(b)·x ≡ log(b^x) and rewrite that as b^x = exp(ln(b)·x)
*/
template<typename TAR, typename VAL>
inline auto
baseScale (Metric targetMetric, Metric srcMetric)
{
using WorkType = CommonComputeType<VAL,TAR>;
if (srcMetric == targetMetric)
return WorkType(1);
auto srcBase = [&]{switch (srcMetric)
{
case BIN: return WorkType(2);
case NAT: return std::exp (WorkType(1));
case DEC: return std::exp (std::log (WorkType(10)) / 10);
default:
throw err::Logic{"Attempt to treat non-logarithmic scales as logarithms"};
break;
}};
return asLogarithm<WorkType> (targetMetric, srcBase());
}
}//(End) log helpers
/**
* Join and combine an additional feed value with an anchor value.
* @note actual joining operation is picked based on both scales and types involved.
@ -226,7 +301,7 @@ namespace par {
if (LIN == metric)
{
if (feedScale.isLogarithmic())
assignConverted (res, value * feedScale.template asFactor<VAL> (srcFeed));
assignConverted (res, value * expFactor<VAL> (feedScale.metric, srcFeed));
else
if (feedScale.isFactor())
assignConverted (res, JoinT(value) * JoinT(srcFeed));
@ -237,10 +312,10 @@ namespace par {
if (this->isLogarithmic())
{
if (feedScale.isLogarithmic())
assignConverted (res, value + feedScale.template baseScale<VAL>(metric) * srcFeed);
assignConverted (res, value + baseScale<VAL,SRC> (metric,feedScale.metric) * srcFeed);
else
if (feedScale.isFactor())
assignConverted (res, value + feedScale.template asLogarithm<VAL> (metric, srcFeed));
assignConverted (res, value + asLogarithm<VAL> (metric, srcFeed));
else
throw err::Invalid {"adding a non-logarithmic data feed on top of a logarithmic base value is pointless."};
}
@ -264,81 +339,5 @@ namespace par {
}
/** transform a logarithmic value into an exponential factor */
template<typename VAL>
template<typename TAR>
inline auto
Scale<VAL>::asFactor (VAL const& value) const
{
using WorkType = CommonComputeType<VAL,TAR>;
switch (metric)
{
case NAT: return std::exp (WorkType(value));
case BIN: return std::exp2 (WorkType(value));
case DEC: return std::pow (WorkType(10), WorkType(value/10.0));
default:
throw err::Logic{_Fmt{"Attempt to compute exponential "
"from a non-logarithmic value with metric %d"}
% metric};
break;
}
}
/** transform this value into a suitable logarithm for the target scale */
template<typename VAL>
template<typename TAR>
inline auto
Scale<VAL>::asLogarithm (Metric targetMetric, VAL const& value) const
{
if (isNeg (value))
throw err::Invalid{_Fmt{"Attempt to take logarithm from %f < 0"}
% value};
using WorkType = CommonComputeType<VAL,TAR>;
switch (targetMetric)
{
case NAT: return std::log (WorkType(value));
case BIN: return std::logb (WorkType(value));
case DEC: return std::log10 (WorkType(value)) * 10;
default:
throw err::Logic{_Fmt{"Attempt to compute logarithm "
"for a non-logarithmic metric %d"}
% targetMetric};
break;
}
}
/** provide the adaptation factor from this logarithmic scale
* to the given other logarithmic scale and target type \a VAL
* @remarks
* - use the logarithm conversion formula logb(x) ≡ logb(k)·logk(x)
* - if target is in decibel, have to apply factor 10; asLogarithm() does that already
* - if we are in decibel, log10 = dB /10; fold that factor in the logarithm of the base,
* by using log(b)·x ≡ log(b^x) and rewrite that as b^x = exp(ln(b)·x)
*/
template<typename VAL>
template<typename TAR>
inline auto
Scale<VAL>::baseScale (Metric targetMetric) const
{
using WorkType = CommonComputeType<VAL,TAR>;
if (metric == targetMetric)
return WorkType(1);
auto myBase = [&]{switch (metric)
{
case NAT: return std::exp(WorkType(1));
case BIN: return WorkType(2);
case DEC: return std::exp(std::log(WorkType(10)) / 10);
default:
throw err::Logic{"Attempt to treat non-logarithmic scales as logarithms"};
break;
}};
return asLogarithm<WorkType> (targetMetric, myBase());
}
}} // namespace lib::par
#endif /*LIB_PAR_SCALE_H*/

View file

@ -155,15 +155,12 @@ _Pragma("GCC diagnostic pop")
template<typename T>
concept number = integral<T> or floating_point<T>;
/**
* the best-precision floating point type to carry out
* a numeric operation to combine two values
*/
template<number V, number W>
using CommonComputeType = std::conditional_t<integral<V> and integral<W>, double
, common_type_t<V, W>>;
namespace {
static_assert (64 <= std::numeric_limits<lflp>::digits
,"long double type can not represent uint64_t exactly");
/** @internal decision logic for combining numeric parameters */
template<typename V, typename W>
auto
@ -180,14 +177,27 @@ _Pragma("GCC diagnostic pop")
if constexpr (8 > sizeof(U))
return std::type_identity<int64_t>{};
else
return std::type_identity<lflo>{};
return std::type_identity<lflp>{};
// no standard integral type can hold uint64_t's full range signed.
// Expand to long double, which can hold 64bit without precision loss
}
}
static_assert (64 <= std::numeric_limits<lflo>::digits
,"long double type can not represent uint64_t exactly");
/** @internal logic to pick a suitable floating-point for intermediary computations */
template<typename V, typename W>
auto
selectCommonComputeType()
{
if constexpr (is_integral_v<V> and is_integral_v<W>)
{ // ensure integral precision is retained to the degree possible...
if constexpr (8 > sizeof(V) and 8 > sizeof(W))
return std::type_identity<double>{};
else
return std::type_identity<lflp>{};
}
else
return std::type_identity<common_type_t<V,W>>{};
}
}
/**
@ -198,8 +208,17 @@ _Pragma("GCC diagnostic pop")
* - in the remaining problematic case(s), long double is used, as it can accommodate
* the full 64bit value range without loss on platforms relevant for Lumiera.
*/
template<typename A, typename B>
using NumberJoinType = typename decltype(selectNumberJoinType<A,B>())::type;
template<number V, number W>
using NumberJoinType = typename decltype(selectNumberJoinType<V,W>())::type;
/**
* best-precision floating point type to carry out
* a numeric operation to transform and adapt value scales.
* @note the result type is always some floating point,
* since the goal is to use the exponential / logarithm family.
*/
template<number V, number W>
using CommonComputeType = typename decltype(selectCommonComputeType<V,W>())::type;

View file

@ -122,12 +122,12 @@ namespace util {
u = 0!=u? u:1;
auto [d,r] = util::iDiv (num, den);
// round to smallest integer fraction, to shake off "number dust"
lflo const ROUND_ULP = 1 + 1/(lflo(std::numeric_limits<int64_t>::max()) * 2);
lflp const ROUND_ULP = 1 + 1/(lflp(std::numeric_limits<int64_t>::max()) * 2);
// construct approximation quantised to 1/u
lflo frac = lflo(r) / den;
lflp frac = lflp(r) / den;
int64_t res = d*u + int64_t(frac*u * ROUND_ULP);
ENSURE (abs (lflo(res)/u - rational_cast<lflo>(Rat{num,den})) <= 1.0/abs(u)
ENSURE (abs (lflp(res)/u - rational_cast<lflp>(Rat{num,den})) <= 1.0/abs(u)
,"Requantisation error exceeded num=%li / den=%li -> res=%li / quant=%li"
, num, den, res, u);
return res;

View file

@ -17,7 +17,7 @@ return: 0
END
PLANNED "Parameter value scales" ParameterScale_test << END
TEST "Parameter value scales" ParameterScale_test << END
return: 0
END

View file

@ -76,7 +76,7 @@ namespace test {
verifyFloatTypes<float>();
verifyFloatTypes<double>();
verifyFloatTypes<lflo>();
verifyFloatTypes<lflp>();
}

View file

@ -39,7 +39,7 @@ namespace test{
// using lumiera::error::LUMIERA_ERROR_LOGIC;
using std::floating_point;
namespace {//Test fixture....
namespace {//Test helpers....
template<typename NUM>
constexpr NUM _MAX = std::numeric_limits<NUM>::max();
@ -50,7 +50,7 @@ namespace test{
template<floating_point FLO>
constexpr FLO _EPSILON = std::numeric_limits<FLO>::epsilon();
}//(End)Test fixture
}//(End)Test helpers

View file

@ -21,13 +21,6 @@
#include "lib/par/scale.hpp"
#include "test/diagnostic-output.hpp"/////////////TODO
//#include <utility>
//#include <string>
//#include <vector>
//using std::string;
//using std::vector;
//using std::swap;
using test::roughEQ;
@ -35,11 +28,18 @@ namespace lib {
namespace par {
namespace test{
// using lumiera::error::LUMIERA_ERROR_LOGIC;
namespace {//Test helpers....
namespace {//Test fixture....
template<typename NUM>
constexpr NUM _MAX = std::numeric_limits<NUM>::max();
}//(End)Test fixture
template<typename NUM>
constexpr NUM _MIN = std::numeric_limits<NUM>::lowest();
template<floating_point FLO>
constexpr FLO _EPSILON = std::numeric_limits<FLO>::epsilon();
}//(End)Test helpers
@ -50,7 +50,13 @@ namespace test{
/**************************************************************************//**
* @test cover properties of generic parameter containers.
* @test demonstrate features and verify behaviour of value Scale definitions.
* - a par::Scale is a descriptor record with some feature configuration settings
* - some combination of setting marks specific scale features to be present
* - values can be _conformed_ to comply with the scale constraints
* - a Scale can be configured to maintain cyclic wrapping values
* - values defined on different scales can be _joined_ (numerically combined)
* - scales can be defined to be logarithmic, and work together with linear scales.
*/
class ParameterScale_test : public Test
{
@ -59,6 +65,7 @@ namespace test{
run (Arg)
{
simpleUsage();
verify_Predicate();
verify_Conforming();
verify_cyclicScale();
verify_valueJoining();
@ -79,6 +86,51 @@ namespace test{
}
/** @test verify the feature detection and descriptor predicates */
void
verify_Predicate()
{
Scale<double> scale;
CHECK (scale.isValid());
CHECK (not scale.isCyclic());
CHECK (not scale.isLimited());
CHECK (not scale.isLogarithmic());
scale.minVal = -5;
CHECK (scale.isValid());
CHECK (not scale.isCyclic());
CHECK ( scale.isLimited());
scale.cyclicLim = 10;
CHECK (not scale.isValid());
CHECK ( scale.isCyclic());
CHECK (not scale.isLimited());
scale.maxVal = +5;
CHECK ( scale.isValid());
CHECK ( scale.isCyclic());
CHECK (not scale.isLimited());
CHECK (not scale.isFactor());
scale.neutral = 2;
CHECK (not scale.isFactor());
scale.neutral = 1;
CHECK ( scale.isFactor());
CHECK (scale.metric == LIN);
CHECK (not scale.isLogarithmic());
scale.metric = NOM;
CHECK (not scale.isLogarithmic());
scale.metric = DEC;
CHECK ( scale.isLogarithmic());
scale.metric = BIN;
CHECK ( scale.isLogarithmic());
scale.metric = NAT;
CHECK ( scale.isLogarithmic());
}
/** @test accommodate values to comply with the scale
* - optionally a maximum and minimum can be enforced
* - optionally the value can be cyclically wrapped
@ -134,7 +186,7 @@ namespace test{
CHECK ( 15 == angleScale.conform(65535));
Scale<lflo> tinyScale = {.minVal=-lflo(1)/100, .maxVal=lflo(1)/100};
Scale<lflp> tinyScale = {.minVal=-lflp(1)/100, .maxVal=lflp(1)/100};
tinyScale.cyclicLim = util::limit_cyclicWrap(*tinyScale.maxVal - *tinyScale.minVal);
CHECK (1e16 < tinyScale.cyclicLim);
CHECK (tinyScale.isValid());
@ -172,24 +224,26 @@ namespace test{
* - applying a linear factor to a logarithmic scale is
* transformed into an addition in the logarithmic domain
* - adding a linear offset to a logarithmic scale is rejected
* @todo WIP 9/26 🔁 define ⟶ ✔ implement
* - extended precision scales handled using `lflp` (≙`long double`)
* - this allows to retain full precision of 64bit integrals
* @todo WIP 9/26 ✔ define ⟶ ✔ implement
*/
void
verify_valueJoining()
{
Scale<ushort> scale = {.minVal = 2, .maxVal = 10};
Scale<int64_t> feedScale;
Scale<int64_t> lscale;
CHECK ( 5 == scale.join (2, int64_t(3), feedScale)); // 2+3 ≡ 5 (all computations done as double)
CHECK (10 == scale.join (5, int64_t(8), feedScale)); // 5+8 ≡ 13 ⟼ capped to 10
CHECK ( 5 == scale.join (2, int64_t(3), lscale)); // 2+3 ≡ 5 (all computations done as double)
CHECK (10 == scale.join (5, int64_t(8), lscale)); // 5+8 ≡ 13 ⟼ capped to 10
feedScale.neutral = 1;
CHECK (feedScale.isFactor());
CHECK ( 6 == scale.join (2, int64_t(3), feedScale)); // 2*3 ≡ 6
CHECK ( 2 == scale.join (2, int64_t(-3),feedScale)); // 2*-3 ≡ -6 ⟼ first conditioned to ushort domain, then capped to 2
lscale.neutral = 1;
CHECK (lscale.isFactor());
CHECK ( 6 == scale.join (2, int64_t(3), lscale)); // 2*3 ≡ 6
CHECK ( 2 == scale.join (2, int64_t(-3),lscale)); // 2*-3 ≡ -6 ⟼ first conditioned to ushort domain, then capped to 2
feedScale.metric = DEC; // in dB
CHECK ( 7 == scale.join (4, int64_t(3), feedScale)); // 4 + 3dB ⟼ 4 * 1.995 ≡ 7.98 ⟼ truncated to 7
lscale.metric = DEC; // in dB
CHECK ( 7 == scale.join (4, int64_t(3), lscale)); // 4 + 3dB ⟼ 4 * 1.995 ≡ 7.98 ⟼ truncated to 7
Scale<short> sscale;
Scale<uint64_t> uscale = {.neutral = 1};
@ -202,28 +256,94 @@ namespace test{
//
Scale<float> fscale = {.metric = NAT};
Scale<float> feed_dB = {.metric = DEC};
CHECK (roughEQ (fscale.join (0, float(20), feed_dB) , log(100.0f) )); // dB ⟼ natural logarithm; note 10dB ≙ factor 10 * lg(10) value divided / 10
CHECK (roughEQ (fscale.join (0, float(20), feed_dB) , log(100.0f) )); // dB ⟼ natural logarithm; note 20dB ≙ factor 10 * lg(100); input 20 divided by 10
fscale.metric = BIN;
CHECK (roughEQ (fscale.join (0, float(20), feed_dB) , log2(100.0f))); // dB ⟼ binary logarithm; (factor 1/10 on input is folded into the log factor)
fscale.metric = DEC;
CHECK (roughEQ (fscale.join (0, float(20), feed_dB) , 20.0f )); // dB ⟼ dB just passed-through and then added to value ≔ 0.0
feedScale.metric = BIN;
CHECK (roughEQ (fscale.join (0, int64_t(-3), feedScale), log10(1.0f/8)*10)); // binary log (given as int value) ⟼ dB
fscale.metric = LIN;
CHECK (roughEQ (fscale.join (1, int64_t(-3), feedScale), 1.0f/8 )); // binary log applied as factor to a linear scale
fscale.metric = DEC;
lscale.metric = BIN;
CHECK (roughEQ (fscale.join (0, int64_t(-3), lscale), log10(1.0f/8) *10)); // binary log (given as int value) ⟼ dB
lscale.metric = NAT;
CHECK (roughEQ (fscale.join (0, int64_t(-3), lscale), log10(exp(-3))*10)); // natural log (input as int) ⟼ dB
lscale.metric = DEC;
CHECK (roughEQ (fscale.join (0, int64_t(-3), lscale), -3 )); // dB ⟼ dB just passed-through
fscale.metric = BIN;
feedScale.metric = LIN;
feedScale.neutral = 1;
CHECK (feedScale.isFactor());
CHECK (fscale.isLogarithmic());
CHECK (1 + 3 == fscale.join (1, int64_t(8), feedScale)); // feed factor 8 ⟼ to binary logarithmic fscale (8 ≡ 2^3) and added there
lscale.metric = BIN;
CHECK (roughEQ (fscale.join (0, int64_t(-3), lscale), -3 )); // lb ⟼ lb just passed-through
lscale.metric = NAT;
CHECK (roughEQ (fscale.join (0, int64_t(-3), lscale), log2 (exp(-3)) )); // natural log ⟼ binary log
lscale.metric = DEC;
CHECK (roughEQ (fscale.join (0, int64_t(-30),lscale), log2 (1.0/1000) )); // conversion factor: dB/10 ⟼ binary log
feedScale.neutral = 0;
CHECK (not feedScale.isFactor());
fscale.metric = LIN;
lscale.metric = BIN;
CHECK (roughEQ (fscale.join (1, int64_t(-3), lscale), 1.0f/8 )); // binary log applied as factor to a linear scale
lscale.metric = NAT;
CHECK (roughEQ (fscale.join (1, int64_t(-1), lscale), 1.0f/exp(1))); // natural log applied as factor 1/e
lscale.metric = DEC;
CHECK (roughEQ (fscale.join (1, int64_t(-30),lscale), 1.0f/1000 )); // decibels applied as factor : -30 dB ≙ 10^-3
fscale.metric = BIN;
lscale.metric = LIN;
lscale.neutral = 1;
CHECK (lscale.isFactor());
CHECK (fscale.isLogarithmic());
CHECK ( fscale.join (1, int64_t(8), lscale) == 1 + 3); // feed factor 8 ⟼ to binary logarithmic fscale (8 ≡ 2^3) and added there (exact)
fscale.metric = NAT;
CHECK (roughEQ (fscale.join (1, int64_t(8), lscale), 1 + logf(8))); // feed factor 8 ⟼ now applied to a natural log scale (and added to one, in float)
fscale.metric = DEC;
CHECK (roughEQ (fscale.join (1, int64_t(100),lscale), 1 + 20)); // feed factor 8 ⟼ now applied as offset in decibels
lscale.neutral = 0;
CHECK (not lscale.isFactor());
VERIFY_FAIL ("adding a non-logarithmic data feed on top of a logarithmic base value"
, fscale.join (1, int64_t(8), feedScale));
, fscale.join (1, int64_t(8), lscale));
//---verify a long-double target scale
//
Scale<lflp> megaScale = {.minVal = -10, .maxVal = +10}; // setup a cyclic scale using the long double domain
megaScale.cyclicLim = util::limit_cyclicWrap<lflp>(20); // (cyclic wrap is handled by conform(); just another little twist)
CHECK (megaScale.isCyclic());
megaScale.metric = DEC;
fscale.metric = LIN;
fscale.neutral = 1;
CHECK (fscale.isFactor());
CHECK ( 5 == megaScale.join ( 5, 100.0f, fscale)); // input ⟼ decibels, then added to the base value, and finally wrapped ∈ [-10 ... 10[
CHECK (-5 == megaScale.join (-5, 100.0f, fscale));
CHECK ( 5 == megaScale.join (-5, 1000.0f, fscale)); // ...so here we get -5dB + 30dB
// now push that to the limits....
CHECK (logb (1e18) < logb (_MAX<int64_t>)); // use the largest power of 10 representable in int64_t
CHECK (logb (_MAX<int64_t>) < logb (megaScale.cyclicLim)); // ...which the (cyclic) target scale can still handle precisely!
lflp res1 = megaScale.join (-5, 1e18f, fscale); // log10 ⟼ 18, so we add -5dB + 180dB;
CHECK (roughEQ (res1, -5)); // the cyclicWrap then computes 175 - (-10) ≡ 185 % 20 (≙5) + (minVal ≙ -10)
// but the computation is not exact...
CHECK (0 != std::fmod (res1, 1));
// yet the cause lies in the input already:
lflp srcErr = lflp(1e18f) - pow (lflp(10), 18);
CHECK (1e10 < fabs (srcErr)); // ... so the float input incurs a gigantic error margin
// however: use an exact input, and the result will be exact
lscale.neutral = 1;
CHECK (lscale.isFactor());
CHECK (-5 == megaScale.join (-5, int64_t(1e18), lscale));
lflp res2 = megaScale.join (-5, int64_t(1e18)+1, lscale);
CHECK (-5 != res2); // even a tiny offset makes it through the computation
CHECK (-16 > log10 (res2 -(-5))); // error is below the 16. decimal place (plausible, since log10 is computed)
// compare to target scale with lesser precision...
Scale<double> doubleTargetScale = {.metric = DEC};
Scale<uint64_t> longTargetScale = {.metric = DEC};
CHECK (180 == doubleTargetScale.join (0, int64_t(1e18), lscale)); // uses double for internal log10 computation
CHECK (180 == doubleTargetScale.join (0, int64_t(1e18)-10, lscale)); // offset chosen large enough to prevail in int64_t but not in double
CHECK (180 == longTargetScale.join (0, int64_t(1e18), lscale)); // 64bit integral scales use long-double for log10 computation internally
CHECK (179 == longTargetScale.join (0, int64_t(1e18)-10, lscale)); // ...and thus the offset survives the log10 and is truncated down on conversion
}

View file

@ -224,7 +224,7 @@ namespace test{
CHECK (format4Csv(double(1) / 3) == "0.333333333333333"_expect );
CHECK (format4Csv(float(1) / 3) == "0.333333"_expect );
CHECK (format4Csv(lflo(1) / 3) == "0.333333333333333333"_expect);
CHECK (format4Csv(lflp(1) / 3) == "0.333333333333333333"_expect);
CHECK (format4Csv(bool(1)) == "true"_expect );
CHECK (format4Csv(bool(0)) == "false"_expect);
CHECK (format4Csv("Starship-3") == "\"Starship-3\""_expect ); // 3rd test today ;-)

View file

@ -92,9 +92,9 @@ namespace test {
CHECK (a2.siz > 555);
// can use that memory without blowing up....
auto doooh = new(a1.mem) lflo{std::numeric_limits<lflo>::max()};
auto doooh = new(a1.mem) lflp{std::numeric_limits<lflp>::max()};
*doooh /= 2;
CHECK (2 == std::numeric_limits<lflo>::max() / *doooh);
CHECK (2 == std::numeric_limits<lflp>::max() / *doooh);
allo.deallocate (a1);
allo.deallocate (a2);

View file

@ -85973,7 +85973,7 @@
<linktarget COLOR="#c50711" DESTINATION="ID_621869035" ENDARROW="Default" ENDINCLINATION="108;0;" ID="Arrow_ID_191032855" SOURCE="ID_1491788669" STARTARROW="None" STARTINCLINATION="34;-37;"/>
<icon BUILTIN="messagebox_warning"/>
</node>
<node CREATED="1789003479184" ID="ID_833220665" MODIFIED="1789003552060" TEXT="The two doors that actually close">
<node CREATED="1789003479184" ID="ID_833220665" MODIFIED="1789003479184" TEXT="The two doors that actually close">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
@ -85985,7 +85985,7 @@
<b>The persisted form</b>. The moment a session file records a parameter with a scale, the meaning of its qualifiers is frozen or needs migration. It's worth spending more thought on what a Scale looks like when written out than on what it looks like in memory.
</li>
<li>
<b>Keeping Scale reducible to data</b>. You mention a possible connection to rules later. A rules system can only reason about a Scale if the Scale <em>is</em>&#160;&#160;a small tuple of facts (metric, bounds, cyclic flag, domain-set id, default) with a fixed interpreter, rather than a polymorphic object whose behaviour exists only as C++. That constraint costs you nothing today, since your qualifier-based design already satisfies it. It's just worth adopting as a deliberate rule so that a future <code>conform()</code>&#160;&#160;override doing something clever doesn't quietly break it.
<b>Keeping Scale reducible to data</b>. You mention a possible connection to rules later. A rules system can only reason about a Scale if the Scale <em>is</em>&#160;a small tuple of facts (metric, bounds, cyclic flag, domain-set id, default) with a fixed interpreter, rather than a polymorphic object whose behaviour exists only as C++. That constraint costs you nothing today, since your qualifier-based design already satisfies it. It's just worth adopting as a deliberate rule so that a future <code><font color="#673a3a">conform()</font></code>&#160;override doing something clever doesn't quietly break it.
</li>
</ul>
</body>
@ -86269,16 +86269,17 @@ class ScaleRegistry
</node>
</node>
</node>
<node CREATED="1789159365103" ID="ID_435108847" MODIFIED="1789159397705" TEXT="sollte dann wohl gleich eine generische Registry sein...">
<node CREATED="1789159365103" ID="ID_435108847" MODIFIED="1790094564624" TEXT="sollte dann wohl gleich eine generische Registry sein...">
<linktarget COLOR="#426aa5" DESTINATION="ID_435108847" ENDARROW="Default" ENDINCLINATION="28;-70;" ID="Arrow_ID_1818399541" SOURCE="ID_735608493" STARTARROW="None" STARTINCLINATION="-393;12;"/>
<linktarget COLOR="#365ac0" DESTINATION="ID_435108847" ENDARROW="Default" ENDINCLINATION="-11;82;" ID="Arrow_ID_246372510" SOURCE="ID_438812693" STARTARROW="None" STARTINCLINATION="14;-8;"/>
<node CREATED="1789159448086" ID="ID_1755995276" MODIFIED="1789159547368" TEXT="Feste Index-Tabelle + heap-allozierte Extents"/>
</node>
</node>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1789170321059" ID="ID_533778146" MODIFIED="1789170328666" TEXT="einfache / vorl&#xe4;ufige Implementierung">
<icon BUILTIN="pencil"/>
<node COLOR="#338800" CREATED="1789170321059" ID="ID_533778146" MODIFIED="1790094388582" TEXT="einfache / vorl&#xe4;ufige Implementierung">
<icon BUILTIN="button_ok"/>
<node CREATED="1789170329999" ID="ID_247689608" MODIFIED="1789170375860" TEXT="&#xbb;vorl&#xe4;ufig&#xab; insofern ich komplexe Themen ignorieren sollte (Nominal-Skalen, Quantisierung)">
<icon BUILTIN="idea"/>
</node>
@ -87235,9 +87236,9 @@ Prop.1: dist%3 1 2 0 1 2 0
<icon BUILTIN="button_ok"/>
</node>
</node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1789171911830" ID="ID_944344547" MODIFIED="1789171915156" TEXT="join">
<icon BUILTIN="pencil"/>
<node COLOR="#338800" CREATED="1789171916490" ID="ID_920699877" MODIFIED="1789172001819" TEXT="einfacher Fall: gemeinsame Scale">
<node COLOR="#338800" CREATED="1789171911830" ID="ID_944344547" MODIFIED="1790094358125" TEXT="join">
<icon BUILTIN="button_ok"/>
<node COLOR="#338800" CREATED="1789171916490" FOLDED="true" ID="ID_920699877" MODIFIED="1790094384518" TEXT="einfacher Fall: gemeinsame Scale">
<icon BUILTIN="button_ok"/>
<node COLOR="#338800" CREATED="1789171933617" ID="ID_284757000" MODIFIED="1789171970154" TEXT="isFactor definieren">
<icon BUILTIN="button_ok"/>
@ -87251,7 +87252,7 @@ Prop.1: dist%3 1 2 0 1 2 0
<icon BUILTIN="button_ok"/>
</node>
</node>
<node COLOR="#338800" CREATED="1789172002831" ID="ID_939833813" MODIFIED="1789961933370" TEXT="gemische Scales handhaben">
<node COLOR="#338800" CREATED="1789172002831" FOLDED="true" ID="ID_939833813" MODIFIED="1790094383113" TEXT="gemische Scales handhaben">
<icon BUILTIN="button_ok"/>
<node COLOR="#338800" CREATED="1789949153983" ID="ID_781783423" MODIFIED="1789949195612" TEXT="ushort-Scale mit Limits &#x27f5; kombiniert mit int64_t">
<icon BUILTIN="button_ok"/>
@ -87263,16 +87264,49 @@ Prop.1: dist%3 1 2 0 1 2 0
<arrowlink COLOR="#28b55c" DESTINATION="ID_1223578931" ENDARROW="Default" ENDINCLINATION="-139;1297;" ID="Arrow_ID_225645373" STARTARROW="None" STARTINCLINATION="111;6;"/>
<icon BUILTIN="button_ok"/>
</node>
<node COLOR="#338800" CREATED="1789961742643" ID="ID_949990390" MODIFIED="1789961760832" TEXT="Verbindung verschiedener log-Scales">
<node COLOR="#338800" CREATED="1789961742643" FOLDED="true" ID="ID_949990390" MODIFIED="1790094370986" TEXT="Verbindung verschiedener log-Scales">
<icon BUILTIN="button_ok"/>
<node COLOR="#435e98" CREATED="1789961765581" ID="ID_1536983378" MODIFIED="1789961783683" TEXT="Umwandlung linear &#x27f7; log"/>
<node COLOR="#435e98" CREATED="1789961765581" ID="ID_1536983378" MODIFIED="1789961783683" TEXT="Umwandlung linear &#x27f7; log">
<node CREATED="1790021597423" ID="ID_5025438" MODIFIED="1790021610941" TEXT="die Konvertierungs-Funktionen sind strukturell eigenartig">
<icon BUILTIN="messagebox_warning"/>
<node CREATED="1790021612645" ID="ID_1614531537" MODIFIED="1790021624855" TEXT="sie h&#xe4;ngen eigentlich zwischen zwei Scales"/>
<node CREATED="1790021853785" ID="ID_1972131978" LINK="https://math.stackexchange.com/questions/14133/clarify-why-all-logarithms-differ-by-a-constant" MODIFIED="1790022525577" TEXT="sie reflektieren die (verwirrende) Struktur der Logarithmen-Konvertierung">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
...ich mu&#223; die Formeln jedes mal wieder nachschlagen, bzw mir herleiten, weil diese Formeln so eigenartig <i>&#252;ber Kreuz</i>&#160;aufgebaut sind:
</p>
<ul>
<li>
<font color="#79320b" face="Monospaced">log</font><font color="#79320b" face="Monospaced" size="2"><i>b</i></font><font color="#79320b" face="Monospaced">(x) &#8801; log</font><font color="#79320b" face="Monospaced" size="2"><i>k</i></font><font color="#79320b" face="Monospaced">(x)/log</font><font color="#79320b" face="Monospaced" size="2"><i>k</i></font><font color="#79320b" face="Monospaced">(b)</font>
</li>
<li>
<font color="#79320b" face="Monospaced">log</font><font color="#79320b" face="Monospaced" size="2"><i>b</i></font><font color="#79320b" face="Monospaced">(x) &#8801; log</font><font color="#79320b" face="Monospaced" size="2"><i>b</i></font><font color="#79320b" face="Monospaced">(k)&#183;log</font><font color="#79320b" face="Monospaced" size="2"><i>k</i></font><font color="#79320b" face="Monospaced">(x)</font>
</li>
</ul>
</body>
</html></richcontent>
</node>
<node CREATED="1790021537463" ID="ID_1193038289" MODIFIED="1790022831555" TEXT="Funktion Scale&lt;VAL&gt;::asLogarithm ist zudem gar nicht von der Quell-Skala abh&#xe4;ngig"/>
</node>
<node BACKGROUND_COLOR="#c8c0b6" COLOR="#435e98" CREATED="1790022911017" ID="ID_761833274" MODIFIED="1790024210457" TEXT="Code-Struktur / Design-Frage : als freie Funktionen?">
<node CREATED="1790022936425" ID="ID_1681792313" MODIFIED="1790023005221" TEXT="als Member-Funktionen spart man sich 2 Parameter in zwei F&#xe4;llen">
<icon BUILTIN="smiley-oh"/>
</node>
<node CREATED="1790022966402" ID="ID_1905759427" MODIFIED="1790022996702" TEXT="allerdings m&#xfc;ssen sie dann auf das Scale-API, und man ruft sie mit &quot;template&quot;-Qualifier auf">
<icon BUILTIN="smiley-neutral"/>
</node>
<node COLOR="#135457" CREATED="1790023008316" ID="ID_1604048781" MODIFIED="1790024202124" TEXT="also: in den sauren Apfel bei&#xdf;en &#x2014; isbesserso">
<icon BUILTIN="yes"/>
</node>
</node>
</node>
<node COLOR="#435e98" CREATED="1789961784810" ID="ID_245445484" MODIFIED="1789961910375" TEXT="Basis-Konvertierung logarithmischer werte"/>
<node COLOR="#435e98" CREATED="1789961796062" ID="ID_1145409092" MODIFIED="1789961910354" TEXT="spezieller Twist: in Decibels steckt ein Faktor 10">
<node CREATED="1789961817086" ID="ID_909916918" MODIFIED="1789961906542" TEXT="wenn als Input verwendet, mu&#xdf; der rausdividiert werden">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
...und dieses Herausdividieren falte ich trickreich in den log-Basis-Konvertierungsfaktor.
@ -87281,13 +87315,80 @@ Prop.1: dist%3 1 2 0 1 2 0
&#220;berraschung: die Logarithmen-Matematik funktioniert ... aber das dumme Mensch braucht endlos um zu kapieren da&#223; die Beispielwerte stimmen
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
<node CREATED="1789961831540" ID="ID_104579195" MODIFIED="1789961843094" TEXT="aber auf der Ergebnis-Seite mu&#xdf; er aufgeschlagen werden"/>
</node>
<node COLOR="#435e98" CREATED="1789961912393" ID="ID_1778190029" MODIFIED="1789961925432" TEXT="linearer Offset auf Logarithmus wird verweigert"/>
<node CREATED="1790029014421" ID="ID_1208653097" MODIFIED="1790036739810" TEXT="Logarithmus-Berechnung in eine long-double-Scale">
<node CREATED="1790036139410" ID="ID_790857236" MODIFIED="1790036256247" TEXT="hier geht es darum, die m&#xf6;glichst pr&#xe4;zise Berechnung zu pr&#xfc;fen">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
die Berechnungen <i>sind</i>&#160;ungenau, mit floating-point und transzendentalen Funktionen. Aber die vorhandene Daten-Genauigkeit sollte ausgesch&#246;pft werden, ohne jetzt gleich alles mit long double rechnen zu m&#252;ssen
</p>
</body>
</html></richcontent>
</node>
<node BACKGROUND_COLOR="#c8c0b6" COLOR="#435e98" CREATED="1790036164854" ID="ID_229924653" MODIFIED="1790094127028" TEXT="im Besonderen die Bestimmung des Arbeits-Datentyps">
<icon BUILTIN="messagebox_warning"/>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#400f69" CREATED="1790037266068" ID="ID_1040329780" MODIFIED="1790094099158" TEXT="tats&#xe4;chlich: die Wahl des Rechen-Datentyps ist zu einfach implementiert">
<linktarget COLOR="#5425ca" DESTINATION="ID_1040329780" ENDARROW="Default" ENDINCLINATION="805;29;" ID="Arrow_ID_1796905346" SOURCE="ID_31887994" STARTARROW="None" STARTINCLINATION="478;26;"/>
<icon BUILTIN="messagebox_warning"/>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1790037396226" HGAP="28" ID="ID_341439483" MODIFIED="1790094090617" TEXT="wir gehen von integral &#x27f6; double" VSHIFT="-7">
<icon BUILTIN="broken-line"/>
</node>
</node>
<node COLOR="#338800" CREATED="1790090085802" ID="ID_222503371" MODIFIED="1790094103279" TEXT="auch hier umstellen auf eine explizit ausformulierte Fall-Logik">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
nicht mehr versuchen, alles in einen Typ-Ausdruck zu packen; stattdessen hier auch die Technik mit einer Constexpr-Funktion, die einen type_identity-Record liefert
</p>
</body>
</html></richcontent>
<icon BUILTIN="button_ok"/>
</node>
</node>
</node>
</node>
<node COLOR="#5928b5" CREATED="1789961961491" ID="ID_219252546" MODIFIED="1790036535745" TEXT="und ja: wer testet, der findet">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<ul>
<li>
Type-promotion-Bug in der Anwendung der pow()-Funktion
</li>
<li>
weiterer Type-promotion-Error in der baseScale-Funktion
</li>
<li>
logb() liefert nur den exponenten, log2() den 2-er-Logarithmus
</li>
<li>
in conform steckt auch noch ein statischer Fehler: std::abs funktioniert nicht auf <i>unsigned</i>
</li>
<li>
das <i>Wiederverwenden</i>&#160;der asLogarithm()-Funktion macht deutlich, da&#223;&#160;diese einen unn&#246;tigen Typ-Constraint auf den VAL-Parameter der Ausgangs-Scale erzwingt
</li>
</ul>
</body>
</html></richcontent>
<arrowlink COLOR="#3f3ba8" DESTINATION="ID_1207373600" ENDARROW="Default" ENDINCLINATION="534;0;" ID="Arrow_ID_625720992" STARTARROW="None" STARTINCLINATION="16;533;"/>
<font ITALIC="true" NAME="SansSerif" SIZE="12"/>
</node>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1790094428926" ID="ID_1432141408" MODIFIED="1790094455904" TEXT="mu&#xdf; nun die Scale (Descriptor) sinnvoll in die Parameter-Implementierung integrieren">
<icon BUILTIN="yes"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1790094459596" ID="ID_184861465" MODIFIED="1790094482634" TEXT="brauche dazu eine Registrierung und Scale-IDs">
<icon BUILTIN="forward"/>
<node CREATED="1790094501963" ID="ID_438812693" MODIFIED="1790094572153" TEXT="und diese Registry sollte generisch angelegt sein">
<arrowlink COLOR="#365ac0" DESTINATION="ID_435108847" ENDARROW="Default" ENDINCLINATION="-11;82;" ID="Arrow_ID_246372510" STARTARROW="None" STARTINCLINATION="14;-8;"/>
</node>
</node>
</node>
@ -87360,8 +87461,8 @@ Prop.1: dist%3 1 2 0 1 2 0
</html></richcontent>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1788896830329" ID="ID_355463973" MODIFIED="1788896840672" TEXT="Scale-Konfiguration erm&#xf6;glichen">
<icon BUILTIN="flag-yellow"/>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1788896830329" ID="ID_355463973" MODIFIED="1790094404823" TEXT="Scale-Konfiguration erm&#xf6;glichen">
<icon BUILTIN="pencil"/>
</node>
</node>
<node CREATED="1788654131472" ID="ID_1807734347" MODIFIED="1788654185033" TEXT="Prototyp-Builder">
@ -87438,7 +87539,7 @@ Prop.1: dist%3 1 2 0 1 2 0
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1788203400185" ID="ID_900611719" MODIFIED="1788307794257" TEXT="Builder-Notation schaffen">
<icon BUILTIN="full-2"/>
</node>
<node COLOR="#435e98" CREATED="1788203422590" ID="ID_1249325546" MODIFIED="1788570267353" TEXT="Valure-Konversionen einbinden">
<node COLOR="#435e98" CREATED="1788203422590" ID="ID_1249325546" MODIFIED="1788570267353" TEXT="Value-Konversionen einbinden">
<icon BUILTIN="full-3"/>
<node COLOR="#338800" CREATED="1788313016806" ID="ID_1966722816" MODIFIED="1788403892705" TEXT="generisch per Indirektion">
<icon BUILTIN="button_ok"/>
@ -87494,8 +87595,8 @@ Prop.1: dist%3 1 2 0 1 2 0
<icon BUILTIN="hourglass"/>
</node>
</node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1789159639409" ID="ID_654373559" MODIFIED="1789607860036" TEXT="Scale-Eigenschaften abstecken per ParameterScale_test">
<icon BUILTIN="pencil"/>
<node COLOR="#338800" CREATED="1789159639409" ID="ID_654373559" MODIFIED="1790094342453" TEXT="Scale-Eigenschaften abstecken per ParameterScale_test">
<icon BUILTIN="button_ok"/>
<node CREATED="1789159687318" ID="ID_1480074646" MODIFIED="1789160197623" TEXT="Hier nur rein die Scale-Descriptoren und das intendierte Verhalten">
<richcontent TYPE="NOTE"><html>
<head/>
@ -87523,6 +87624,11 @@ Prop.1: dist%3 1 2 0 1 2 0
<icon BUILTIN="button_ok"/>
</node>
</node>
<node COLOR="#338800" CREATED="1790094258921" ID="ID_1942061887" MODIFIED="1790094261651" TEXT="verify_Predicate">
<icon BUILTIN="button_ok"/>
<node CREATED="1790094263246" ID="ID_1388987426" MODIFIED="1790094274333" TEXT="demonstriert die wichtigsten Informations-Funktionen"/>
<node COLOR="#310088" CREATED="1790094276841" ID="ID_783774183" MODIFIED="1790094312786" TEXT="(isValid() wird i.d.R. mit dem jeweilgen Feature zusammen abgedeckt)"/>
</node>
<node COLOR="#338800" CREATED="1789607293094" ID="ID_734632281" MODIFIED="1789607296849" TEXT="verify_Conforming">
<icon BUILTIN="button_ok"/>
<node CREATED="1789607298013" ID="ID_452068382" MODIFIED="1789607555871" TEXT="im Grunde offensichtlich und redundant">
@ -87558,8 +87664,131 @@ Prop.1: dist%3 1 2 0 1 2 0
<icon BUILTIN="yes"/>
</node>
</node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1789675604211" ID="ID_201071963" MODIFIED="1789676070358" TEXT="verify_valueJoining">
<icon BUILTIN="pencil"/>
<node COLOR="#338800" CREATED="1789675604211" ID="ID_201071963" MODIFIED="1790094240877" TEXT="verify_valueJoining">
<icon BUILTIN="button_ok"/>
<node COLOR="#338800" CREATED="1790015115678" ID="ID_1510723485" MODIFIED="1790015142255" TEXT="Schritt-1 : teste Typ-Konvertierung und Zusammenspiel mit conform()">
<icon BUILTIN="button_ok"/>
</node>
<node COLOR="#338800" CREATED="1790015143816" ID="ID_1173177816" MODIFIED="1790015164942" TEXT="Schritt-2 : kl&#xe4;re und repariere das noch bestehende signed/unsigned-Problem">
<icon BUILTIN="button_ok"/>
</node>
<node COLOR="#338800" CREATED="1790015165677" ID="ID_1207373600" MODIFIED="1790094237018" TEXT="Schritt-2 : Pfadabdeckung der Logarithmus-Skalen-Behandlung">
<linktarget COLOR="#3f3ba8" DESTINATION="ID_1207373600" ENDARROW="Default" ENDINCLINATION="534;0;" ID="Arrow_ID_625720992" SOURCE="ID_219252546" STARTARROW="None" STARTINCLINATION="16;533;"/>
<icon BUILTIN="button_ok"/>
<node COLOR="#8c0157" CREATED="1790024320509" HGAP="56" ID="ID_1433168831" MODIFIED="1790028991933" TEXT="l&#xe4;stig aber sinnvoll..." VSHIFT="21">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
jaja ... oft wei&#223; man nicht ob es um &quot;overengieering&quot; geht, oder man selber einfach <i>faul</i>&#160;ist
</p>
</body>
</html></richcontent>
<icon BUILTIN="smiley-oh"/>
</node>
<node COLOR="#338800" CREATED="1790028975682" FOLDED="true" HGAP="66" ID="ID_228688702" LINK="#ID_1208653097" MODIFIED="1790094232863" TEXT="auch noch einen log-double-Fall" VSHIFT="-1">
<icon BUILTIN="button_ok"/>
<node COLOR="#435e98" CREATED="1790036164854" ID="ID_585649034" MODIFIED="1790094228089" TEXT="Augenmerk auf Arbeits-Datentyp legen">
<icon BUILTIN="yes"/>
</node>
<node CREATED="1790036260115" ID="ID_1393622284" MODIFIED="1790036281875" TEXT="f&#xfc;hre hier als &#xbb;gimmick&#xab; einen zyklische long-double-Skala ein">
<node CREATED="1790036283183" ID="ID_181132589" MODIFIED="1790036293637" TEXT="damit k&#xf6;nnte ich exakte kleine Werte bekommen"/>
<node CREATED="1790036295430" ID="ID_803016212" MODIFIED="1790036306537" TEXT="und somit Rechenfehler dramatisch sichtbar machen"/>
<node CREATED="1790036308659" ID="ID_1027526075" MODIFIED="1790036324753" TEXT="(und au&#xdf;erdem best&#xe4;tigt das nochmal die Integration von conform())">
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1790036331633" ID="ID_926231651" MODIFIED="1790036487239" TEXT="Ha! Compile-Fehler">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
woot?&#160;&#160;kann keinen std::abs berechnen von .... uint64_t&#160;&#160;??!
</p>
<p>
&quot;der mutt dat doch checken!&quot;
</p>
</body>
</html></richcontent>
<icon BUILTIN="broken-line"/>
</node>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#5e0f69" CREATED="1790036344862" ID="ID_1357077453" MODIFIED="1790037322238" TEXT="in conform() mu&#xdf; der Genauigkeits-Test zur compile-Zeit geguarded werden">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
Tats&#228;chlich ist der ganze Zweig mit dem Genauigkeits-Limit nur f&#252;r floating-point-Typen notwendig; und diesen Branch-Guard kann man tats&#228;chlich schon zur compile-time machen!
</p>
</body>
</html></richcontent>
<icon BUILTIN="idea"/>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#b7d8bb" COLOR="#338800" CREATED="1790036598900" ID="ID_246190994" MODIFIED="1790094211855" TEXT="ein echter Logarithmus ist am &#xbb;intereessantesten&#xab;">
<icon BUILTIN="idea"/>
<node CREATED="1790036619370" ID="ID_913914685" MODIFIED="1790036638956">
<richcontent TYPE="NODE"><html>
<head/>
<body>
<p>
also nehme ich einen <b>linearen </b>Input
</p>
</body>
</html></richcontent>
</node>
<node CREATED="1790036640983" ID="ID_1799300764" MODIFIED="1790036674383" TEXT="und wende den als Faktor auf eine Decibel-Skala an">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
damit's raucht!
</p>
</body>
</html></richcontent>
</node>
<node CREATED="1790036690621" ID="ID_296790938" MODIFIED="1790036696747" TEXT="teste erst mal mit float-Input">
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1790036837717" ID="ID_918464928" MODIFIED="1790036869516" TEXT="verbl&#xfc;ffend: mit kleinem Input, zB. 1000 ist das Ergebnis exakt">
<icon BUILTIN="messagebox_warning"/>
</node>
<node CREATED="1790036877098" ID="ID_1895824050" MODIFIED="1790036888026" TEXT="im Debugger sieht man aber ganz hinten schon die Rundungsfehler"/>
<node CREATED="1790036889038" ID="ID_1642131477" MODIFIED="1790036914456" TEXT="nur das fmod in der Cyclic-Scale schluckt wohl die Subnormals">
<icon BUILTIN="help"/>
</node>
<node CREATED="1790036918375" ID="ID_873113976" MODIFIED="1790036933655" TEXT="mit einem sehr gro&#xdf;en Input wird der Fehler sehr deutlich">
<node CREATED="1790036950358" ID="ID_1610030443" MODIFIED="1790036982693" TEXT="verwende einen Input von 10^18">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
das ist die gr&#246;&#223;tm&#246;gliche 10-er-Potenz, die noch in 63bit reinpa&#223;t
</p>
</body>
</html></richcontent>
</node>
<node CREATED="1790037016669" ID="ID_1010388251" MODIFIED="1790037055912" TEXT="dann ist der Rechenfehler am Ende &#x2248; 10^10"/>
</node>
</node>
<node CREATED="1790037065135" ID="ID_719693034" MODIFIED="1790037072092" TEXT="nehme dann int64_t-Input">
<node COLOR="#338800" CREATED="1790037078616" ID="ID_1172597143" MODIFIED="1790037090915" TEXT="nun l&#xe4;uft selbst diese extreme Berechnung exakt">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1790037118015" ID="ID_886617373" MODIFIED="1790037148321" TEXT="bekomme nun 10^18 + 1 noch als abweichendes Signal durch die Berechnung"/>
<node CREATED="1790037158306" ID="ID_819609666" MODIFIED="1790037170188" TEXT="und der Fehler ist &lt; 10^16"/>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1790037172872" ID="ID_1473906602" MODIFIED="1790037181400" TEXT="klingt plausibel">
<icon BUILTIN="yes"/>
</node>
</node>
<node CREATED="1790037201845" ID="ID_1001721326" MODIFIED="1790037216271" TEXT="Gegencheck: ungenauere Target-Scale">
<node CREATED="1790037220688" ID="ID_1408762592" MODIFIED="1790037236453" TEXT="double &#x27f9; kann diesen Fehler wie erwartet nicht &#xbb;fangen&#xab;"/>
<node COLOR="#435e98" CREATED="1790037238039" ID="ID_31887994" MODIFIED="1790094163676" TEXT="uint64_t &#x27f9; auch nicht &#x27f5; das ist nicht plausibel">
<arrowlink COLOR="#5425ca" DESTINATION="ID_1040329780" ENDARROW="Default" ENDINCLINATION="805;29;" ID="Arrow_ID_1796905346" STARTARROW="None" STARTINCLINATION="478;26;"/>
<icon BUILTIN="broken-line"/>
</node>
</node>
</node>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#d2beaf" COLOR="#5c4d6e" CREATED="1790094336075" ID="ID_814063222" MODIFIED="1790094337810" TEXT="verify_NominalScale">
<icon BUILTIN="hourglass"/>
</node>
</node>
</node>