Parameters: implementation for metric conversions

LIN -> LIN yet either additive or multiplicative
LOG -> LIN
LIN -> LOG
and different kinds of logarithms

all computations using a common intermediary numeric type,
preferably a suitably sized floating-point
This commit is contained in:
Fischlurch 2026-09-20 02:12:06 +02:00
parent 16bb331be0
commit 129eb0c1bd
4 changed files with 218 additions and 26 deletions

View file

@ -42,6 +42,7 @@ namespace lib {
namespace par { namespace par {
namespace err = lumiera::error; namespace err = lumiera::error;
using util::_Fmt;
using std::optional; using std::optional;
@ -78,6 +79,7 @@ namespace par {
bool isValid() const; bool isValid() const;
bool isFactor() const; bool isFactor() const;
bool isCyclic() const; bool isCyclic() const;
bool isLogarithmic() const;
/* ===== conforming operations ===== */ /* ===== conforming operations ===== */
@ -88,15 +90,16 @@ namespace par {
VAL join (VAL const&, SRC const&, Scale<SRC> const&) const; VAL join (VAL const&, SRC const&, Scale<SRC> const&) const;
VAL join (VAL const&, VAL const&) const; VAL join (VAL const&, VAL const&) const;
private: /* ===== conversion helpers ===== */
template<typename TAR>
VAL asFactor (VAL const&) const;
template<typename TAR> template<typename TAR>
VAL baseScale (Metric targetMetric) const; auto asFactor (VAL const&) const;
template<typename TAR> template<typename TAR>
VAL asLogarithm (Metric targetMetric, VAL const&) const; auto baseScale (Metric targetMetric) const;
template<typename TAR>
auto asLogarithm (Metric targetMetric, VAL const&) const;
}; };
@ -153,6 +156,15 @@ namespace par {
return VAL(0) != cyclicLim; return VAL(0) != cyclicLim;
} }
template<typename VAL>
inline bool
Scale<VAL>::isLogarithmic() const
{
return metric == DEC
or metric == NAT
or metric == BIN;
}
/* ===== conforming operations ===== */ /* ===== conforming operations ===== */
@ -181,9 +193,9 @@ namespace par {
REQUIRE (*minVal < *maxVal); REQUIRE (*minVal < *maxVal);
if (std::is_floating_point_v<VAL>) if (std::is_floating_point_v<VAL>)
if (not (std::abs (rawVal) < cyclicLim)) if (not (std::abs (rawVal) < cyclicLim))
throw err::Invalid {util::_Fmt{"Parameter value %4.2g beyond supported numeric precision " throw err::Invalid {_Fmt{"Parameter value %4.2g beyond supported numeric precision "
"for cyclic wrapping into [%1.2g...%1.2g[ "} "for cyclic wrapping into [%1.2g...%1.2g[ "}
% rawVal % *minVal % *maxVal % rawVal % *minVal % *maxVal
}; };
return util::cyclicWrap (rawVal, *minVal, *maxVal); return util::cyclicWrap (rawVal, *minVal, *maxVal);
} }
@ -213,7 +225,7 @@ namespace par {
if (feedScale.isLogarithmic()) if (feedScale.isLogarithmic())
assignConverted (res, value * feedScale.template asFactor<VAL> (srcFeed)); assignConverted (res, value * feedScale.template asFactor<VAL> (srcFeed));
else else
if (this->isFactor()) if (feedScale.isFactor())
assignConverted (res, value * srcFeed); assignConverted (res, value * srcFeed);
else else
assignConverted (res, value + srcFeed); assignConverted (res, value + srcFeed);
@ -247,28 +259,77 @@ namespace par {
); );
} }
/** transform a logarithmic value into an exponential factor */
template<typename VAL> template<typename VAL>
template<typename TAR> template<typename TAR>
inline VAL inline auto
Scale<VAL>::asFactor (VAL const& value) const Scale<VAL>::asFactor (VAL const& value) const
{ {
UNIMPLEMENTED ("transform a logarithmic value into an exponential factor"); 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, 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 VAL>
template<typename TAR> template<typename TAR>
inline VAL inline auto
Scale<VAL>::baseScale (Metric targetMetric) const
{
UNIMPLEMENTED ("provide the adaptation factor from this logarithmic scale to the given other logarithmic scale");
}
template<typename VAL>
template<typename TAR>
inline VAL
Scale<VAL>::asLogarithm (Metric targetMetric, VAL const& value) const Scale<VAL>::asLogarithm (Metric targetMetric, VAL const& value) const
{ {
UNIMPLEMENTED ("transform this value into a suitable logarithm for the target scale"); 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());
} }

View file

@ -42,8 +42,9 @@ namespace par {
using util::isNeg; using util::isNeg;
using std::same_as; using std::same_as;
using std::floating_point; using std::integral;
using std::signed_integral; using std::signed_integral;
using std::floating_point;
using std::numeric_limits; using std::numeric_limits;
@ -146,6 +147,18 @@ _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 T, number U>
using CommonComputeType = std::conditional_t<integral<T> and integral<U>, double
, std::common_type_t<T, U>>;
}} // namespace lib::par }} // namespace lib::par
#endif /*LIB_PAR_TYPE_HANDLER_H*/ #endif /*LIB_PAR_TYPE_HANDLER_H*/

View file

@ -61,6 +61,7 @@ namespace test{
simpleUsage(); simpleUsage();
verify_Conforming(); verify_Conforming();
verify_cyclicScale(); verify_cyclicScale();
verify_valueJoining();
} }
@ -156,13 +157,28 @@ namespace test{
} }
/** @test /** @test Base value and data feeds from differing Scales can be joined together
* @todo WIP 9/26 🔁 define ⟶ implement * @todo WIP 9/26 🔁 define ⟶ implement
*/ */
void void
verify_valueJoining() verify_valueJoining()
{ {
Scale<float> scale; Scale<ushort> scale = {.minVal = 2, .maxVal = 10};
Scale<int64_t> feedScale;
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
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
feedScale.metric = DEC; // in dB
CHECK ( 7 == scale.join (4, int64_t(3), feedScale)); // 4 + 3dB ⟼ 4 * 1.995 ≡ 7.98 ⟼ truncated to 7
////////////////////////////OOO die folgende Konvertierung entgleist....
SHOW_EXPR(short(-3) * uint64_t(2))
} }

View file

@ -85656,13 +85656,41 @@
<node CREATED="1789690632759" ID="ID_169878566" MODIFIED="1789690643602" TEXT="sonst &#x27f9; Feed addieren"/> <node CREATED="1789690632759" ID="ID_169878566" MODIFIED="1789690643602" TEXT="sonst &#x27f9; Feed addieren"/>
</node> </node>
<node CREATED="1789690650447" ID="ID_763848405" MODIFIED="1789690934324" TEXT="Quelle multiplikativ &#x27f9; logarithmieren und addieren"/> <node CREATED="1789690650447" ID="ID_763848405" MODIFIED="1789690934324" TEXT="Quelle multiplikativ &#x27f9; logarithmieren und addieren"/>
<node CREATED="1789690952586" ID="ID_806764948" MODIFIED="1789690977990" TEXT="sonst delogarithmieren, Feed addieren, re-logarithmieren"/> <node COLOR="#5b280f" CREATED="1789690952586" ID="ID_806764948" MODIFIED="1789864844352" TEXT="sonst delogarithmieren, Feed addieren, re-logarithmieren">
<icon BUILTIN="button_cancel"/>
<node CREATED="1789864846110" ID="ID_1307210282" MODIFIED="1789865000390" TEXT="dieser Fall macht inhaltlich keinen Sinn">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
man k&#246;nnte hier zwar delogarithmieren / relogarithmieren, aber was ist z.B. bei negativem Ergebnis...? und, noch mehr: welchen Sinn hat es zu einem <i>multiplikativen Faktor</i>&#160;etwas hinzuzuaddieren (was aber nicht logarithmisch ist)
</p>
</body>
</html>
</richcontent>
</node>
<node CREATED="1789864856488" ID="ID_1180276604" MODIFIED="1789865677798" TEXT="diese Kombination kann eigentlich nur auf einen konzeptionellen Fehler hinweisen">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
Logarithmische Metrik verwendet man nur, wenn auch die Gr&#246;&#223;e, in ihrer Bedeutung f&#252;r die Wahrnehmung, selber logarithmisch ist. Zwar k&#246;nnte man sich rein-formal vorstellen, da&#223; so ein Fall durchaus mal auftreten kann &#8212; aber dann w&#228;re es besser, die Situation zu hinterfragen: fehlt da vielleicht die Markierung des Feed als <i>logarithmisch?</i>
</p>
</body>
</html>
</richcontent>
</node>
</node>
</node> </node>
<node CREATED="1789691026066" ID="ID_1521422421" MODIFIED="1789691072009" TEXT="Ziel ordinal und Quelle additiv(linear) &#x27f9; Ordinal-Step"/> <node CREATED="1789691026066" ID="ID_1521422421" MODIFIED="1789691072009" TEXT="Ziel ordinal und Quelle additiv(linear) &#x27f9; Ordinal-Step"/>
<node CREATED="1789691081395" ID="ID_1029470467" MODIFIED="1789691128539" TEXT="Ziel nominal und Quelle nominal oder linear &#x27f9; Feed(codiert) ersetzt Quelle"/> <node CREATED="1789691081395" ID="ID_1029470467" MODIFIED="1789691128539" TEXT="Ziel nominal und Quelle nominal oder linear &#x27f9; Feed(codiert) ersetzt Quelle"/>
</node> </node>
<node CREATED="1789691154922" ID="ID_1884775055" MODIFIED="1789691157587" TEXT="Berechnung"> <node CREATED="1789691154922" ID="ID_1884775055" MODIFIED="1789691157587" TEXT="Berechnung">
<node CREATED="1789691206195" ID="ID_521077468" MODIFIED="1789691213662" TEXT="Genauigkeit steigern"> <node CREATED="1789691206195" ID="ID_521077468" MODIFIED="1789877446133" TEXT="Berechnung mit passender Genauigkeit">
<node CREATED="1789691368028" ID="ID_1992504549" MODIFIED="1789691686368" TEXT="de/Logarithmieren : stets bestm&#xf6;glichen Floating-Point"> <node CREATED="1789691368028" ID="ID_1992504549" MODIFIED="1789691686368" TEXT="de/Logarithmieren : stets bestm&#xf6;glichen Floating-Point">
<node CREATED="1789691697643" ID="ID_1724775924" MODIFIED="1789691708345" TEXT="von Integraltypen &#x27f9; stets double"/> <node CREATED="1789691697643" ID="ID_1724775924" MODIFIED="1789691708345" TEXT="von Integraltypen &#x27f9; stets double"/>
<node CREATED="1789691709035" ID="ID_28970072" MODIFIED="1789691791715" TEXT="ansonsten maximalen floating-point-Typ"> <node CREATED="1789691709035" ID="ID_28970072" MODIFIED="1789691791715" TEXT="ansonsten maximalen floating-point-Typ">
@ -85678,6 +85706,80 @@
</html> </html>
</richcontent> </richcontent>
</node> </node>
<node COLOR="#435e98" CREATED="1789869944433" ID="ID_1523483246" MODIFIED="1789877469677" TEXT="sollte das als Typedef ausdr&#xfc;cken: CommonComputeType&lt;T,U&gt;">
<icon BUILTIN="yes"/>
<node CREATED="1789869964502" ID="ID_1289831616" MODIFIED="1789869996988" TEXT="geht nicht als Serie von constrained templates">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
...weil sich die Constraints typischerweise &#252;berlappen
</p>
</body>
</html>
</richcontent>
</node>
<node CREATED="1789869998707" ID="ID_221126714" MODIFIED="1789870032835" TEXT="dann doch die Bedingung als std::contditional_t ausformulieren">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<div style="background-color: #eee0b5; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 2px">
<div style="color: #202020; background-color: #eee0b5; font-family: DejaVu Sans Mono; font-size: 9pt; white-space: pre">
<p style="margin-top: 0; margin-right: 0; margin-bottom: 0; margin-left: 0">
<font color="#4a1a65"><b>template</b></font><font color="#65533c">&lt;</font><font color="#4a1a65"><b>typename</b></font><font color="#3a1f18">&#160;</font><font color="#a34649"><b><u>T</u></b></font><font color="#65533c">&gt;</font>
</p>
<p style="margin-top: 0; margin-right: 0; margin-bottom: 0; margin-left: 0">
<font color="#4a1a65"><b>concept</b></font><font color="#3a1f18">&#160;number </font><font color="#65533c">=</font><font color="#3a1f18">&#160;</font><font color="#59253b">integral&lt;T&gt;</font><font color="#3a1f18">&#160; </font><font color="#4a1a65"><b>or</b></font><font color="#3a1f18">&#160;</font><font color="#59253b">floating_point&lt;T&gt;</font><font color="#65533c">;</font>
</p>
<p style="margin-top: 0; margin-right: 0; margin-bottom: 0; margin-left: 0">
<br />
</p>
<p style="margin-top: 0; margin-right: 0; margin-bottom: 0; margin-left: 0">
<font color="#b88b63">/**</font>
</p>
<p style="margin-top: 0; margin-right: 0; margin-bottom: 0; margin-left: 0">
<font color="#b88b63">* the best-precision floating point type to carry out</font>
</p>
<p style="margin-top: 0; margin-right: 0; margin-bottom: 0; margin-left: 0">
<font color="#b88b63">* a numeric operation to combine two values</font>
</p>
<p style="margin-top: 0; margin-right: 0; margin-bottom: 0; margin-left: 0">
<font color="#b88b63">*/</font>
</p>
<p style="margin-top: 0; margin-right: 0; margin-bottom: 0; margin-left: 0">
<font color="#4a1a65"><b>template</b></font><font color="#65533c">&lt;</font><font color="#3a1f18">number </font><font color="#a34649"><b><u>T</u></b></font><font color="#65533c">,</font><font color="#3a1f18">&#160; number </font><font color="#a34649"><b><u>U</u></b></font><font color="#65533c">&gt;</font>
</p>
<p style="margin-top: 0; margin-right: 0; margin-bottom: 0; margin-left: 0">
<font color="#4a1a65"><b>using</b></font><font color="#3a1f18">&#160;</font><font color="#8c505c"><b>CommonComputeType</b></font><font color="#3a1f18">&#160;</font><font color="#65533c">=</font><font color="#3a1f18">&#160; </font><font color="#535773">std</font><font color="#65533c">::</font><font color="#8c505c"><b>conditional_t</b></font><font color="#65533c">&lt;</font><font color="#59253b">integral&lt;T&gt;</font><font color="#3a1f18">&#160; </font><font color="#4a1a65"><b>and</b></font><font color="#3a1f18">&#160;</font><font color="#59253b">integral&lt;U&gt;</font><font color="#65533c">,</font><font color="#3a1f18">&#160;</font><font color="#7f004c"><b>double</b></font>
</p>
<p style="margin-top: 0; margin-right: 0; margin-bottom: 0; margin-left: 0">
<font color="#65533c">,</font><font color="#3a1f18">&#160;</font><font color="#535773">std</font><font color="#65533c">::</font><font color="#8c505c"><b>common_type_t</b></font><font color="#65533c">&lt;</font><font color="#a34649"><b><u>T</u></b></font><font color="#65533c">,</font><font color="#3a1f18">&#160; </font><font color="#a34649"><b><u>U</u></b></font><font color="#65533c">&gt;&gt;;</font>
</p>
<p style="margin-top: 0; margin-right: 0; margin-bottom: 0; margin-left: 0">
</p>
</div>
</div>
</body>
</html>
</richcontent>
<icon BUILTIN="idea"/>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1789876376322" ID="ID_1223578931" MODIFIED="1789876404350" TEXT="eigentliche Join-Operation in auf passendem Zwischentyp ausf&#xfc;hren">
<icon BUILTIN="pencil"/>
<node CREATED="1789876405765" ID="ID_1109556835" MODIFIED="1789876417629" TEXT="CommonComputeType verwenden"/>
<node BACKGROUND_COLOR="#fafe99" COLOR="#fa002a" CREATED="1789876423793" ID="ID_603151342" MODIFIED="1789877266018" TEXT="Vorsicht Falle: Kombination von signed/unsigned">
<icon BUILTIN="broken-line"/>
<node CREATED="1789877276449" ID="ID_1205492042" MODIFIED="1789877286892" TEXT="kann mich nicht allein auf die number-promotion verlassen"/>
<node CREATED="1789877306308" ID="ID_1833375564" MODIFIED="1789877330237" TEXT="Gegenbeispiel: short(-3) * uint64_t(2)"/>
</node>
</node> </node>
<node CREATED="1789691232896" ID="ID_575039816" MODIFIED="1789691803456" TEXT="Konvertierung in den Ziel-Typ stets zuletzt (vor Conform)"/> <node CREATED="1789691232896" ID="ID_575039816" MODIFIED="1789691803456" TEXT="Konvertierung in den Ziel-Typ stets zuletzt (vor Conform)"/>
</node> </node>