Lib: cover re-quantisation helper

...which I intend to use for sanitising poisonous rational numbers,
as prerequisite for handling divisor based time scales in the ZoomWindow
This commit is contained in:
Fischlurch 2022-11-15 02:13:57 +01:00
parent ce1220ee72
commit cfe3a6618f
3 changed files with 107 additions and 23 deletions

View file

@ -97,6 +97,7 @@ namespace util {
* in the same order of magnitude (which is surprising). This function gets
* slightly faster for smaller data types. The naive bitshift-count implementation
* is always significantly slower (8 times for int64_t, 1.6 times for int8_t)
* @see Rational_test::verify_intLog2()
* @see ZoomWindow_test
*
* [ToddLehman]: https://stackoverflow.com/users/267551/todd-lehman
@ -155,17 +156,21 @@ namespace util {
* causing numeric overflow when used, even just additively.
* This function can thus be used to _"sanitise"_ a number,
* and thus accept a small error while preventing overflow.
* @note using extended-precision floating point to get close to the
* quantiser even for large denominator. Some platforms
* fall back to double, leading to extended error bounds
*/
inline Rat
reQuant (Rat src, int64_t u)
{
int64_t d = rational_cast<int64_t> (src);
int64_t r = src.numerator() % src.denominator();
using f128 = long double;
// construct approximation quantised to 1/u
double frac = rational_cast<double> (Rat{r, src.denominator()});
f128 frac = rational_cast<f128> (Rat{r, src.denominator()});
Rat res = d + int64_t(frac*u) / Rat(u);
ENSURE (abs (rational_cast<double>(src) - rational_cast<double>(res)) < 1.0/abs(u));
ENSURE (abs (rational_cast<f128>(src) - rational_cast<f128>(res)) <= 1.0/abs(u));
return res;
}
} // namespace util

View file

@ -28,15 +28,9 @@
#include "lib/error.hpp"
#include "lib/test/run.hpp"
#include "lib/format-obj.hpp"
#include "lib/format-cout.hpp"
#include "lib/rational.hpp"
#include "lib/format-cout.hpp"//////////////TODO
#define SHOW_TYPE(_TY_) \
cout << "typeof( " << STRINGIFY(_TY_) << " )= " << lib::meta::typeStr<_TY_>() <<endl;
#define SHOW_EXPR(_XX_) \
cout << "#--◆--# " << STRINGIFY(_XX_) << " ? = " << _XX_ <<endl;
//////////////////////////////////////////////////////////////////////////////TODO
#include <chrono>
#include <array>
@ -194,7 +188,7 @@ namespace test {
/**
* @test a slightly optimised implementation of integer binary logarithm
* @test an optimised implementation of integer binary logarithm
* - basically finds the highest bit which is set
* - can be used with various integral types
* - performs better than using the floating-point solution
@ -269,7 +263,7 @@ namespace test {
array<uint64_t, 1000> numz;
for (auto& n : numz)
{
n = rand() * uint64_t(2147483648);
n = rand() * uint64_t(1 << 31);
CHECK (ilog2(n) == floatLog(n));
CHECK (ilog2(n) == bitshift(n));
}
@ -301,8 +295,8 @@ namespace test {
<< "std::ilogb :"<<time_float<<"ns"<<endl
<< "bit-shift :"<<time_shift<<"ns"<<endl
<< "identity :"<<time_ident<<"ns"<<endl
<< "(checksum="<<dump<<")" <<endl; // Warning: without outputting `dump`, compiler would optimise away most calls
<< "(checksum="<<dump<<")" <<endl; // Warning: without outputting `dump`,
// the optimiser would eliminate most calls
// the following holds both with -O0 and -O3
CHECK (time_ilog2 < time_shift);
CHECK (time_ident < time_ilog2);
@ -335,7 +329,42 @@ namespace test {
void
verify_requant()
{
const int64_t MAX{std::numeric_limits<int64_t>::max()};
const Rat MAXI = Rat{MAX};
Rat poison = (MAXI-88)/(MAXI/7);
auto approx = [](Rat rat){ return rational_cast<float> (rat); };
CHECK (poison > 0);
CHECK (poison+1 < 0); // wrap around!
CHECK (approx (poison ) == 6.99999952f); // wildly wrong results...
CHECK (approx (poison+1) == -6);
CHECK (approx (poison+7) == -6.83047369e-17f);
CHECK (approx (poison+9_r/5) == 0.400000006f);
Rat sleazy = reQuant (poison, 1 << 24); // recast into multiples of an arbitrary other divisor,
CHECK (sleazy.denominator() == 1 << 24); // (here using a power of two as example)
// and now we can do all the slick stuff...
CHECK (sleazy > 0);
CHECK (sleazy+1 > 0);
CHECK (sleazy+7 > 0);
CHECK (approx (sleazy) == 7);
CHECK (approx (sleazy+1) == 8);
CHECK (approx (sleazy+7) == 14);
CHECK (approx (sleazy+9_r/5) == 8.80000019f);
CHECK (util::toString (poison) == "9223372036854775719/1317624576693539401sec");
CHECK (util::toString (poison+1) =="-7905747460161236496/1317624576693539401sec");
CHECK (util::toString (sleazy) == "117440511/16777216sec");
CHECK (util::toString (sleazy+1) == "134217727/16777216sec");
// also works towards larger denominator, or with negative numbers...
CHECK (reQuant (poison, MAX-7) == 104811045873349724_r/14973006553335675);
CHECK (reQuant (-poison, 7777) == -54438_r/ 7777);
CHECK (reQuant (poison, -7777) == -54438_r/-7777);
CHECK (approx (reQuant (poison, MAX-7)) == 7);
CHECK (approx (reQuant (poison, 7777)) == 6.99987125f);
}
};

View file

@ -39874,9 +39874,9 @@
<linktarget COLOR="#784489" DESTINATION="ID_1438689210" ENDARROW="Default" ENDINCLINATION="202;-30;" ID="Arrow_ID_1396674797" SOURCE="ID_159285221" STARTARROW="None" STARTINCLINATION="-243;119;"/>
<icon BUILTIN="info"/>
<node CREATED="1668295785449" ID="ID_248479209" MODIFIED="1668295794255" TEXT="Nenner gegen Limit pr&#xfc;fen"/>
<node COLOR="#338800" CREATED="1668295795329" ID="ID_1179855526" MODIFIED="1668353517806" TEXT="Requantisieren">
<node COLOR="#338800" CREATED="1668295795329" FOLDED="true" ID="ID_1179855526" MODIFIED="1668474431508" TEXT="Requantisieren">
<icon BUILTIN="button_ok"/>
<node CREATED="1668353519645" ID="ID_1709187608" MODIFIED="1668353551219" TEXT="so exakt wie m&#xf6;glich">
<node CREATED="1668353519645" ID="ID_1709187608" MODIFIED="1668474384018" TEXT="so exakt wie m&#xf6;glich">
<richcontent TYPE="NOTE"><html>
<head>
@ -39887,17 +39887,20 @@
</p>
</body>
</html></richcontent>
<linktarget COLOR="#f2fbd7" DESTINATION="ID_1709187608" ENDARROW="Default" ENDINCLINATION="113;234;" ID="Arrow_ID_229027216" SOURCE="ID_505458679" STARTARROW="Default" STARTINCLINATION="-412;-14;"/>
<icon BUILTIN="stop-sign"/>
</node>
<node CREATED="1668353552561" ID="ID_1127388689" MODIFIED="1668353567293" TEXT="n&#xe4;herungsweise per double">
<node COLOR="#435e98" CREATED="1668353552561" ID="ID_1127388689" MODIFIED="1668474254609" TEXT="n&#xe4;herungsweise per long double">
<icon BUILTIN="idea"/>
<node CREATED="1668353569055" ID="ID_1784438485" MODIFIED="1668353581617" TEXT="aufteilen in Ganzzahl und Modulus"/>
<node CREATED="1668353584405" ID="ID_1181209360" MODIFIED="1668353593384" TEXT="dann den Modulus n&#xe4;herungsweise requantifizieren"/>
<node COLOR="#338800" CREATED="1668353653955" ID="ID_1020200002" MODIFIED="1668353816582" TEXT="Neben-Untersuchung: Ganzzahl-Zweierlogarithmus">
<node COLOR="#338800" CREATED="1668353653955" FOLDED="true" ID="ID_1020200002" MODIFIED="1668353816582" TEXT="Neben-Untersuchung: Ganzzahl-Zweierlogarithmus">
<icon BUILTIN="button_ok"/>
<node CREATED="1668353668892" ID="ID_259284183" MODIFIED="1668353679884" TEXT="ja... die banale L&#xf6;sung mit der Bitshift-Schleife"/>
<node CREATED="1668353680848" ID="ID_152151776" MODIFIED="1668353792957" TEXT="Geht das auch besser? vielleicht logarithmisch?">
<node CREATED="1668353698198" ID="ID_394250867" LINK="https://stackoverflow.com/a/24748637" MODIFIED="1668353698198" TEXT="sowiso..."/>
<node CREATED="1668353698198" ID="ID_394250867" LINK="https://stackoverflow.com/a/24748637" MODIFIED="1668353698198" TEXT="sowiso...">
<linktarget COLOR="#ffe1c1" DESTINATION="ID_394250867" ENDARROW="Default" ENDINCLINATION="-128;27;" ID="Arrow_ID_791821354" SOURCE="ID_812438903" STARTARROW="None" STARTINCLINATION="-142;-24;"/>
</node>
<node CREATED="1668353698198" ID="ID_275694038" LINK="http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogObvious" MODIFIED="1668353698198" TEXT="aber klar doch..."/>
</node>
<node COLOR="#338800" CREATED="1668353793968" ID="ID_9788121" MODIFIED="1668353882082" TEXT="eine Adaption dieser Methode mal als Utility formuliert">
@ -39940,6 +39943,7 @@
<node CREATED="1668396009603" ID="ID_1438298126" MODIFIED="1668396019050" TEXT="f&#xfc;r int64_t">
<node CREATED="1668393519910" ID="ID_219605808" MODIFIED="1668397317940" TEXT="mein ilog2: 5.6ns"/>
<node CREATED="1668393672305" ID="ID_812438903" MODIFIED="1668393802739" TEXT="Vorlage SO: 7.6ns">
<arrowlink COLOR="#ffe1c1" DESTINATION="ID_394250867" ENDARROW="Default" ENDINCLINATION="-128;27;" ID="Arrow_ID_791821354" STARTARROW="None" STARTINCLINATION="-142;-24;"/>
<node CREATED="1668395067578" ID="ID_386803191" MODIFIED="1668395176399" TEXT="das liegt aber nur am fehlenden -1 Branch">
<richcontent TYPE="NOTE"><html>
<head>
@ -40003,23 +40007,69 @@
<node CREATED="1668353619784" ID="ID_395391770" MODIFIED="1668353624739" TEXT="nicht klar ob relevant"/>
<node CREATED="1668353625375" ID="ID_27295187" MODIFIED="1668353643696" TEXT="dann auch Micro-Benchmark notwendig"/>
</node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1668353892092" ID="ID_1654551581" MODIFIED="1668353948869" TEXT="Direkte L&#xf6;sung + Fehler-Assert">
<icon BUILTIN="pencil"/>
<node COLOR="#338800" CREATED="1668353892092" ID="ID_1654551581" MODIFIED="1668474307633" TEXT="Direkte L&#xf6;sung + Fehler-Assert">
<icon BUILTIN="button_ok"/>
<node CREATED="1668353919128" ID="ID_1237619949" MODIFIED="1668353937498" TEXT="ohne weitere Sanity-Checks"/>
<node COLOR="#338800" CREATED="1668353938677" ID="ID_278768234" MODIFIED="1668353944961" TEXT="funktioniert im Smoke-Test">
<icon BUILTIN="button_ok"/>
</node>
<node COLOR="#435e98" CREATED="1668474259440" ID="ID_1390713963" MODIFIED="1668474302021" TEXT="verwende long double">
<icon BUILTIN="idea"/>
<node CREATED="1668474283161" ID="ID_505458679" MODIFIED="1668474406928">
<richcontent TYPE="NODE"><html>
<head>
</head>
<body>
<p>
damit kann ich stets so genau wie m&#246;glich rechnen
</p>
</body>
</html></richcontent>
<arrowlink COLOR="#f2fbd7" DESTINATION="ID_1709187608" ENDARROW="Default" ENDINCLINATION="113;234;" ID="Arrow_ID_229027216" STARTARROW="Default" STARTINCLINATION="-412;-14;"/>
</node>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1668474285441" ID="ID_318301259" MODIFIED="1668474305669" TEXT="Risiko: manche Plattformen definieren long double == double">
<icon BUILTIN="messagebox_warning"/>
</node>
<node CREATED="1668474317488" ID="ID_1121607924" MODIFIED="1668474342956" TEXT="erh&#xf6;he deshalb den Fehler-Assert auf einschli&#xdf;lich Grenze">
<icon BUILTIN="yes"/>
</node>
</node>
</node>
</node>
<node COLOR="#338800" CREATED="1668353832314" ID="ID_1509712772" MODIFIED="1668353882082" TEXT="Test-Methoden ob ein Bruch &#xbb;giftig&#xab; ist">
<linktarget COLOR="#07a5b2" DESTINATION="ID_1509712772" ENDARROW="Default" ENDINCLINATION="96;-4;" ID="Arrow_ID_1066574831" SOURCE="ID_9788121" STARTARROW="None" STARTINCLINATION="-282;15;"/>
<icon BUILTIN="button_ok"/>
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1668354018194" HGAP="39" ID="ID_1484604434" MODIFIED="1668354032742" TEXT="Testabdeckung..." VSHIFT="14">
<icon BUILTIN="flag-pink"/>
<node COLOR="#338800" CREATED="1668354018194" HGAP="39" ID="ID_1484604434" MODIFIED="1668474197855" TEXT="Testabdeckung..." VSHIFT="14">
<icon BUILTIN="button_ok"/>
<node COLOR="#338800" CREATED="1668474155145" ID="ID_132764069" MODIFIED="1668474234849" TEXT="ein Beispiel mit Nenner nahe INT_MAX">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
Rat poison = (MAXI-88)/(MAXI/7);
</p>
<p>
...das ist n&#228;herungsweise Sieben...
</p>
</body>
</html></richcontent>
<icon BUILTIN="button_ok"/>
</node>
<node COLOR="#338800" CREATED="1668474201756" ID="ID_1997612449" MODIFIED="1668474234849" TEXT="Quantisieren auf feineren Divisor">
<icon BUILTIN="button_ok"/>
</node>
<node COLOR="#338800" CREATED="1668474224181" ID="ID_721417455" MODIFIED="1668474234849" TEXT="negative Zahlen">
<icon BUILTIN="button_ok"/>
</node>
</node>
</node>
<node CREATED="1668295799991" ID="ID_192811111" MODIFIED="1668295828000" TEXT="Z&#xe4;hler gegen Limit pr&#xfc;fen und ggfs kappen"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1668295799991" ID="ID_192811111" MODIFIED="1668474435369" TEXT="Z&#xe4;hler gegen Limit pr&#xfc;fen und ggfs kappen">
<icon BUILTIN="flag-yellow"/>
</node>
</node>
</node>
</node>