Compare commits

...

2 commits

Author SHA1 Message Date
0320c31eb5 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
4f8a8e8042 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 `lflo` 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
12 changed files with 91 additions and 22 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 (f128 val) noexcept { return showFloatingPoint (val, PRECISION_DECIMAL<f128>); }
string showDecimal (lflo val) noexcept { return showFloatingPoint (val, PRECISION_DECIMAL<lflo>); }
string showComplete (double val) noexcept { return showFloatingPoint (val, PRECISION_COMPLETE<double>); }
string showComplete (float val) noexcept { return showFloatingPoint (val, PRECISION_COMPLETE<float>); }
string showComplete (f128 val) noexcept { return showFloatingPoint (val, PRECISION_COMPLETE<f128>); }
string showComplete (lflo val) noexcept { return showFloatingPoint (val, PRECISION_COMPLETE<lflo>); }
string

View file

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

View file

@ -32,8 +32,8 @@ using llong = long long int;
using ullong = unsigned long long int;
using ushort = unsigned short int;
using f128 = long double;
static_assert(10 <= sizeof(f128));
using lflo = long double;
static_assert(10 <= sizeof(lflo));
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 (f128) noexcept;
std::string showDecimal (lflo) noexcept;
/** show enough decimal digits to represent every distinct value */
std::string showComplete (double) noexcept;
std::string showComplete (float) noexcept;
std::string showComplete (f128) noexcept;
std::string showComplete (lflo) noexcept;
std::string showSize (size_t) noexcept;

View file

@ -277,7 +277,7 @@ namespace par {
{
case NAT: return std::exp (WorkType(value));
case BIN: return std::exp2 (WorkType(value));
case DEC: return std::pow<WorkType> (10, value/10.0);
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"}

View file

@ -180,11 +180,14 @@ _Pragma("GCC diagnostic pop")
if constexpr (8 > sizeof(U))
return std::type_identity<int64_t>{};
else
return std::type_identity<f128>{};
return std::type_identity<lflo>{};
// 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");
}
/**

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"
f128 const ROUND_ULP = 1 + 1/(f128(std::numeric_limits<int64_t>::max()) * 2);
lflo const ROUND_ULP = 1 + 1/(lflo(std::numeric_limits<int64_t>::max()) * 2);
// construct approximation quantised to 1/u
f128 frac = f128(r) / den;
lflo frac = lflo(r) / den;
int64_t res = d*u + int64_t(frac*u * ROUND_ULP);
ENSURE (abs (f128(res)/u - rational_cast<f128>(Rat{num,den})) <= 1.0/abs(u)
ENSURE (abs (lflo(res)/u - rational_cast<lflo>(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

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

View file

@ -116,7 +116,7 @@ namespace test{
/** @test Scales can be configured to wrap cyclically, e.g. for angles.
* - demonstrate an unsigned angle scale with short precision
* - demonstrate a high-resolution scale symmetric to zero
* @todo WIP 9/26 ✔define ⟶ 🔁 implement
* @todo WIP 9/26 ✔define ⟶ ✔ implement
*/
void
verify_cyclicScale()
@ -134,7 +134,7 @@ namespace test{
CHECK ( 15 == angleScale.conform(65535));
Scale<f128> tinyScale = {.minVal=-f128(1)/100, .maxVal=f128(1)/100};
Scale<lflo> tinyScale = {.minVal=-lflo(1)/100, .maxVal=lflo(1)/100};
tinyScale.cyclicLim = util::limit_cyclicWrap(*tinyScale.maxVal - *tinyScale.minVal);
CHECK (1e16 < tinyScale.cyclicLim);
CHECK (tinyScale.isValid());
@ -158,7 +158,21 @@ namespace test{
/** @test Base value and data feeds from differing Scales can be joined together
* @todo WIP 9/26 🔁 define ⟶ implement
* - when the target scale defines limits (or is cyclic),
* this conforming is applied after all conversions
* - a feed can be marked as "factor", in which case
* the feed is multiplied to the target rather than added
* - even scales with integral type can carry log meaning
* - suitable intermediary types used for good precision,
* and especially to avoid wrapping signed into unsigned
* - the supported logarithmic flavours are accommodated,
* possibly converting the base of the logarithm accordingly
* - the factor 10 inherent to decibels is applied / removed
* before combining decibel values with other logarithms
* - 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
*/
void
verify_valueJoining()
@ -183,6 +197,33 @@ namespace test{
// Note: number promotion rules of C++ would backfire nastily here...
CHECK (short(-3) * uint64_t(2) > numeric_limits<int64_t>::max());
CHECK (short(-6) == sscale.join (-3, uint64_t(2), uscale));
//---floating-point logarithmic scale conversions---
//
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
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 = 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
feedScale.neutral = 0;
CHECK (not feedScale.isFactor());
VERIFY_FAIL ("adding a non-logarithmic data feed on top of a logarithmic base value"
, fscale.join (1, int64_t(8), feedScale));
}

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(f128(1) / 3) == "0.333333333333333333"_expect);
CHECK (format4Csv(lflo(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) f128{std::numeric_limits<f128>::max()};
auto doooh = new(a1.mem) lflo{std::numeric_limits<lflo>::max()};
*doooh /= 2;
CHECK (2 == std::numeric_limits<f128>::max() / *doooh);
CHECK (2 == std::numeric_limits<lflo>::max() / *doooh);
allo.deallocate (a1);
allo.deallocate (a2);

View file

@ -87251,8 +87251,8 @@ Prop.1: dist%3 1 2 0 1 2 0
<icon BUILTIN="button_ok"/>
</node>
</node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1789172002831" ID="ID_939833813" MODIFIED="1789949264299" TEXT="gemische Scales handhaben">
<icon BUILTIN="pencil"/>
<node COLOR="#338800" CREATED="1789172002831" ID="ID_939833813" MODIFIED="1789961933370" 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"/>
</node>
@ -87263,6 +87263,31 @@ 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">
<icon BUILTIN="button_ok"/>
<node COLOR="#435e98" CREATED="1789961765581" ID="ID_1536983378" MODIFIED="1789961783683" TEXT="Umwandlung linear &#x27f7; log"/>
<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>
<body>
<p>
...und dieses Herausdividieren falte ich trickreich in den log-Basis-Konvertierungsfaktor.
</p>
<p>
&#220;berraschung: die Logarithmen-Matematik funktioniert ... aber das dumme Mensch braucht endlos um zu kapieren da&#223; die Beispielwerte stimmen
</p>
</body>
</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>
</node>
</node>
</node>