Parameters: document and verify integration into cyclic Scale

- verify using an unsigned integer, for a typical ''angle scale''
- also verify a high-res floating-point scale, and demonstrate rounding issues
This commit is contained in:
Fischlurch 2026-09-16 23:04:28 +02:00
parent eef5ddef0e
commit 30636b3253
5 changed files with 136 additions and 17 deletions

View file

@ -119,7 +119,9 @@ namespace par {
) )
) )
and (not isCyclic() and (not isCyclic()
or (minVal and maxVal and *minVal < *maxVal) or (minVal and maxVal
and *minVal < *maxVal
and VAL(0) < cyclicLim)
) )
; ;
} }
@ -169,8 +171,8 @@ namespace par {
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 {util::_Fmt{"Parameter value %4.2g beyond supported numeric precision "
"for cyclic wrapping into (%4.2g ... %4.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);
} }

View file

@ -39,6 +39,7 @@
#include "lib/random.hpp" #include "lib/random.hpp"
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include <concepts>
#include <typeinfo> #include <typeinfo>
#include <cstdlib> #include <cstdlib>
#include <utility> #include <utility>
@ -61,14 +62,15 @@ namespace test{
using util::showType; using util::showType;
using util::showTypes; using util::showTypes;
using std::floating_point;
constexpr auto ROUGH_PRECISION = pow (10, -3); constexpr auto ROUGH_PRECISION = pow (10, -3);
constexpr auto EPSILON_ULP = 5; constexpr auto EPSILON_ULP = 5;
template<typename F, typename N> template<floating_point F, typename N>
constexpr inline enable_if< std::is_floating_point<F>, constexpr inline
bool > bool
roughEQ (F val, N target, F limit =ROUGH_PRECISION) roughEQ (F val, N target, F limit =ROUGH_PRECISION)
{ {
REQUIRE (0 < limit); REQUIRE (0 < limit);
@ -76,9 +78,9 @@ namespace test{
} //////////////////////////////////////////////////////////////////////////TICKET #1360 looks like this problem was solved several times } //////////////////////////////////////////////////////////////////////////TICKET #1360 looks like this problem was solved several times
template<typename F> template<floating_point F>
constexpr inline enable_if< std::is_floating_point<F>, constexpr inline
F > F
ulp (F val) ulp (F val)
{ {
val = fabs (val); val = fabs (val);
@ -90,9 +92,9 @@ namespace test{
return scaledUlp; return scaledUlp;
} }
template<typename F, typename N> template<floating_point F, typename N>
constexpr inline enable_if< std::is_floating_point<F>, constexpr inline
bool > bool
epsEQ (F val, N target, uint ulps =EPSILON_ULP) epsEQ (F val, N target, uint ulps =EPSILON_ULP)
{ {
return abs (val - target) < ulps * ulp<F> (target); return abs (val - target) < ulps * ulp<F> (target);

View file

@ -20,7 +20,6 @@
#include "lib/util-quant.hpp" #include "lib/util-quant.hpp"
#include "lib/util.hpp" #include "lib/util.hpp"
using ::Test;
using util::isLimited; using util::isLimited;
using std::floating_point; using std::floating_point;

View file

@ -17,7 +17,7 @@
#include "test/run.hpp" #include "test/run.hpp"
//#include "test/test-helper.hpp" #include "test/test-helper.hpp"
#include "lib/par/scale.hpp" #include "lib/par/scale.hpp"
#include "test/diagnostic-output.hpp"/////////////TODO #include "test/diagnostic-output.hpp"/////////////TODO
@ -28,6 +28,7 @@
//using std::string; //using std::string;
//using std::vector; //using std::vector;
//using std::swap; //using std::swap;
using test::roughEQ;
namespace lib { namespace lib {
@ -59,6 +60,7 @@ namespace test{
{ {
simpleUsage(); simpleUsage();
verify_Conforming(); verify_Conforming();
verify_cyclicScale();
} }
@ -76,8 +78,10 @@ namespace test{
} }
/** @test /** @test accommodate values to comply with the scale
* @todo WIP 9/26 🔁 define ⟶ implement * - optionally a maximum and minimum can be enforced
* - optionally the value can be cyclically wrapped
* @todo WIP 9/26 ✔ define ⟶ ✔ implement
*/ */
void void
verify_Conforming() verify_Conforming()
@ -87,7 +91,79 @@ namespace test{
scale.maxVal = -5.0f; scale.maxVal = -5.0f;
CHECK (-5.0f == scale.conform (1e5f)); CHECK (-5.0f == scale.conform (1e5f));
CHECK (scale.isValid());
scale.minVal = 5.0f;
CHECK (not scale.isValid());
scale.maxVal = 8.0f;
CHECK (scale.isValid());
CHECK (5.0f == scale.conform (-5.0f));
CHECK (not scale.isCyclic());
scale.cyclicLim = 100;
CHECK (scale.isCyclic());
CHECK (7.0f == scale.conform (-5.0f));
CHECK (5.0f == scale.conform (-4.0f));
CHECK (6.0f == scale.conform (-3.0f));
CHECK (7.0f == scale.conform (-2.0f));
VERIFY_FAIL ("beyond supported numeric precision"
, scale.conform (-1e5f));
} }
/** @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
*/
void
verify_cyclicScale()
{
Scale<ushort> angleScale = {.minVal=0, .maxVal=360, .cyclicLim=0xFFFF};
CHECK (angleScale.isValid());
CHECK (angleScale.isCyclic());
CHECK ( 0 == angleScale.conform(0));
CHECK (180 == angleScale.conform(180));
CHECK (240 == angleScale.conform(240));
CHECK ( 0 == angleScale.conform(360));
CHECK (359 == angleScale.conform(719));
CHECK ( 0 == angleScale.conform(65520));
CHECK ( 15 == angleScale.conform(65535));
Scale<f128> tinyScale = {.minVal=-f128(1)/100, .maxVal=f128(1)/100};
tinyScale.cyclicLim = util::limit_cyclicWrap(*tinyScale.maxVal - *tinyScale.minVal);
CHECK (1e16 < tinyScale.cyclicLim);
CHECK (tinyScale.isValid());
CHECK (tinyScale.isCyclic());
CHECK ( tinyScale.conform(0 ) == 0 );
CHECK (roughEQ (tinyScale.conform(0.001) , 0.001));
CHECK (roughEQ (tinyScale.conform(0.005) , 0.005));
CHECK (roughEQ (tinyScale.conform(0.00999), 0.00999));
CHECK (roughEQ (tinyScale.conform(0.01 ),-0.0099999)); // surprise: 1/100 not exactly representable in floating-point
// since the domain is symmetric and 2/100 long, the ten-folds will fall into the middle,
// but they typically do not land precisely on zero due to inherent "number dust"
CHECK (1e15 > fabs(tinyScale.conform(1)));
CHECK (0 != tinyScale.conform(1) );
auto beyond_precision = -10 * tinyScale.cyclicLim;
VERIFY_FAIL ("beyond supported numeric precision"
, tinyScale.conform (beyond_precision));
}
/** @test
* @todo WIP 9/26 🔁 define ⟶ implement
*/
void
verify_valueJoining()
{
Scale<float> scale;
}
}; };

View file

@ -87231,7 +87231,7 @@ Prop.1: dist%3 1 2 0 1 2 0
<icon BUILTIN="hourglass"/> <icon BUILTIN="hourglass"/>
</node> </node>
</node> </node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1789159639409" ID="ID_654373559" MODIFIED="1789159678163" TEXT="Scale-Eigenschaften abstecken per ParameterScale_test"> <node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1789159639409" ID="ID_654373559" MODIFIED="1789607860036" TEXT="Scale-Eigenschaften abstecken per ParameterScale_test">
<icon BUILTIN="pencil"/> <icon BUILTIN="pencil"/>
<node CREATED="1789159687318" ID="ID_1480074646" MODIFIED="1789160197623" TEXT="Hier nur rein die Scale-Descriptoren und das intendierte Verhalten"> <node CREATED="1789159687318" ID="ID_1480074646" MODIFIED="1789160197623" TEXT="Hier nur rein die Scale-Descriptoren und das intendierte Verhalten">
<richcontent TYPE="NOTE"><html> <richcontent TYPE="NOTE"><html>
@ -87260,6 +87260,46 @@ Prop.1: dist%3 1 2 0 1 2 0
<icon BUILTIN="button_ok"/> <icon BUILTIN="button_ok"/>
</node> </node>
</node> </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">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
...da ein Test eigentlich mehr machen sollte, als nur nochmal den Code zu wiederholen; und hier passiert nicht mehr, als ein cap an der boundrary, auf einem komplett offensichtlichen und einfachen Code-Pfad
</p>
</body>
</html>
</richcontent>
</node>
<node CREATED="1789607312019" ID="ID_1154758301" MODIFIED="1789607325207" TEXT="aber man k&#xf6;nnte einen anderen Datentyp testen (z.B. Float)"/>
<node CREATED="1789607333006" ID="ID_1782430086" MODIFIED="1789607343123" TEXT="und zeigen da&#xdf; auch das cyclicWrap hier integriert ist"/>
</node>
<node COLOR="#338800" CREATED="1789607351998" ID="ID_144363848" MODIFIED="1789607354317" TEXT="verify_cyclicScale">
<icon BUILTIN="button_ok"/>
<node CREATED="1789607356261" ID="ID_1762601340" MODIFIED="1789607743052" TEXT="auch hier nur bedingt sinnvoll testbar">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
...denn die eigentlich trickreiche Funktionalit&#228;t steckt in der Library-Funktion und ist dort schon per Unit-Test abgedeckt, und das korrekte Setup wird innerhalb des Scale-Descriptors gar nicht gepr&#252;ft (weil diese Pr&#252;fung in den umschlie&#223;enden Kontext geh&#246;rt, wo die Scale in einen Parameter-Typ eingebunden wird)
</p>
</body>
</html></richcontent>
</node>
<node CREATED="1789607368451" ID="ID_383818308" MODIFIED="1789607375932" TEXT="kann zwei relevante F&#xe4;lle demonstrieren">
<node CREATED="1789607377402" ID="ID_1342839519" MODIFIED="1789607401203" TEXT="Winkel-Skala (unsigned short)"/>
<node CREATED="1789607407654" ID="ID_1837015555" MODIFIED="1789607428439" TEXT="floating-point-Scala mit kleinen Werten"/>
</node>
<node COLOR="#435e98" CREATED="1789607809345" ID="ID_1668358580" MODIFIED="1789607849725" TEXT="diese Tests dienen eigentlich nur dem Integrations-Check &#x2014; und sind Dokumentation">
<icon BUILTIN="yes"/>
</node>
</node>
</node> </node>
</node> </node>
</node> </node>