Timeline: safely calculate sum/difference of large fractional times
...in a similar vein as done for the product calculation. In this case, we need to check the dimensions carefully and pick the best calculation path, but as long as the overall result can be represented, it should be possible to carry out the calculation with fractional values, albeit introducing a small error. As a follow-up, I have now also refactored the re-quantisation functions, to be usable for general requantisation to another grid, and I used these to replace the *naive* implementation of the conversion FSecs -> µ-Grid, which caused a lot of integer-wrap-around However, while the test now works basically without glitch or wrap, the window position is still numerically of by 1e-6, which becomes quite noticeably here due to the large overall span used for the test.
This commit is contained in:
parent
7007101357
commit
289f92da7e
7 changed files with 326 additions and 21 deletions
|
|
@ -73,6 +73,7 @@
|
|||
#include <stdint.h>
|
||||
#include <boost/rational.hpp>
|
||||
|
||||
#include "lib/util-quant.hpp"
|
||||
|
||||
|
||||
namespace util {
|
||||
|
|
@ -154,6 +155,39 @@ namespace util {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Re-Quantise a number into a new grid, truncating to the next lower grid point.
|
||||
* @remark Grid-aligned values can be interpreted as rational numbers (integer fractions),
|
||||
* where the quantiser corresponds to the denominator and the numerator counts
|
||||
* the number of grid steps. To work both around precision problems and the
|
||||
* danger of integer wrap-around, the integer division is performed on the
|
||||
* old value and then the re-quantisation done on the remainder, using
|
||||
* 128bit floating point for maximum precision. This operation can
|
||||
* also be used to re-form a fraction to be cast in terms of the
|
||||
* new quantiser; this introduces a tiny error, but typically
|
||||
* allows for safe or simplified calculations.
|
||||
* @param num the count in old grid-steps (#den) or the numerator
|
||||
* @param den the old quantiser or the denominator of a fraction
|
||||
* @param u the new quantiser or the new denominator to use
|
||||
* @return the adjusted numerator, so that the fraction with u
|
||||
* will be almost the same than dividing #num by #den
|
||||
*/
|
||||
inline int64_t
|
||||
reQuant (int64_t num, int64_t den, int64_t u)
|
||||
{
|
||||
u = 0!=u? u:1;
|
||||
auto [d,r] = util::iDiv (num, den);
|
||||
using f128 = long double;
|
||||
// round to smallest integer fraction, to shake off "number dust"
|
||||
f128 const ROUND_ULP = 1 + 1/(f128(std::numeric_limits<int64_t>::max()) * 2);
|
||||
|
||||
// construct approximation quantised to 1/u
|
||||
f128 frac = f128(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));
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* re-Quantise a rational number to a (typically smaller) denominator.
|
||||
* @param u the new denominator to use
|
||||
|
|
@ -170,15 +204,7 @@ namespace util {
|
|||
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
|
||||
f128 frac = rational_cast<f128> (Rat{r, src.denominator()});
|
||||
Rat res = d + int64_t(frac*u) / Rat(u);
|
||||
ENSURE (abs (rational_cast<f128>(src) - rational_cast<f128>(res)) <= 1.0/abs(u));
|
||||
return res;
|
||||
return Rat{reQuant (src.numerator(), src.denominator(), u), u};
|
||||
}
|
||||
} // namespace util
|
||||
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@
|
|||
#include "lib/error.hpp"
|
||||
#include "lib/time.h"
|
||||
#include "lib/time/timevalue.hpp"
|
||||
#include "lib/rational.hpp"
|
||||
#include "lib/util-quant.hpp"
|
||||
#include "lib/format-string.hpp"
|
||||
|
||||
|
|
@ -336,7 +337,10 @@ lumiera_tmpbuf_print_time (gavl_time_t time)
|
|||
gavl_time_t
|
||||
lumiera_rational_to_time (FSecs const& fractionalSeconds)
|
||||
{
|
||||
return rational_cast<gavl_time_t> (fractionalSeconds * lib::time::TimeValue::SCALE);
|
||||
return gavl_time_t(util::reQuant (fractionalSeconds.numerator()
|
||||
,fractionalSeconds.denominator()
|
||||
,lib::time::TimeValue::SCALE
|
||||
));
|
||||
}
|
||||
|
||||
gavl_time_t
|
||||
|
|
|
|||
|
|
@ -78,6 +78,13 @@ namespace util {
|
|||
{ }
|
||||
};
|
||||
|
||||
template<typename I>
|
||||
inline IDiv<I>
|
||||
iDiv (I num, I den) ///< support type inference and auto typing...
|
||||
{
|
||||
return IDiv<I>{num,den};
|
||||
}
|
||||
|
||||
|
||||
/** floor function for integer arithmetics.
|
||||
* Unlike the built-in integer division, this function
|
||||
|
|
|
|||
|
|
@ -116,6 +116,7 @@ namespace model {
|
|||
|
||||
using util::min;
|
||||
using util::max;
|
||||
using util::sgn;
|
||||
|
||||
namespace { ///////////////////////////////////////////////////////////////////////////////////////////////TICKET #1259 : reorganise raw time base datatypes : need conversion path into FSecs
|
||||
/**
|
||||
|
|
@ -143,6 +144,12 @@ namespace model {
|
|||
% duration.denominator();
|
||||
}
|
||||
|
||||
inline double
|
||||
approx (Rat r)
|
||||
{
|
||||
return util::rational_cast<double> (r);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1261 : why the hell did I define time entities to be immutable. Looks like a "functional programming" fad in hindsight
|
||||
/** @todo we need these only because the blurry distinction between
|
||||
* lib::time::TimeValue and lib::time::Time, which in turn is caused
|
||||
|
|
@ -521,12 +528,13 @@ namespace model {
|
|||
static FSecs
|
||||
scaleSafe (FSecs duration, Rat factor)
|
||||
{
|
||||
auto approx = [](Rat r){ return rational_cast<double> (r); };
|
||||
|
||||
if (not util::can_represent_Product(duration, factor))
|
||||
{
|
||||
if (approx(MAX_TIMESPAN) < approx(duration) * approx (factor))
|
||||
return MAX_TIMESPAN; // exceeds limits of time representation => cap the result
|
||||
auto guess{approx(duration) * approx (factor)};
|
||||
if (approx(MAX_TIMESPAN) < abs(guess))
|
||||
return MAX_TIMESPAN * sgn(guess); // exceeds limits of time representation => cap the result
|
||||
if (0 == guess)
|
||||
return 0;
|
||||
|
||||
// slightly adjust the factor so that the time-base denominator cancels out,
|
||||
// allowing to calculate the product without dangerous multiplication of large numbers
|
||||
|
|
@ -538,6 +546,52 @@ namespace model {
|
|||
return duration * factor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate sum (or difference) of possibly large time durations, avoiding integer wrap-around.
|
||||
* Again, this is a heuristics, based on re-quantisation to a smaller common denominator.
|
||||
* @return exact result if representable, otherwise approximation
|
||||
* @note result is capped to MAX_TIMESPAN when exceeding domain
|
||||
*/
|
||||
static FSecs
|
||||
addSafe (FSecs t1, FSecs t2)
|
||||
{
|
||||
if (not util::can_represent_Sum (t1,t2))
|
||||
{
|
||||
auto guess{approx(t1) + approx(t2)};
|
||||
if (approx(MAX_TIMESPAN) < abs(guess))
|
||||
return MAX_TIMESPAN * sgn(guess); // exceeds limits => cap the result
|
||||
|
||||
// re-Quantise numbers to achieve a common denominator,
|
||||
// thus avoiding to multiply numerators for normalisation
|
||||
int64_t n1 = t1.numerator();
|
||||
int64_t d1 = t1.denominator();
|
||||
int s1 = sgn(n1)*sgn(d1);
|
||||
n1 = abs(n1); d1 = abs(d1);
|
||||
int64_t n2 = t2.numerator();
|
||||
int64_t d2 = t2.denominator();
|
||||
int s2 = sgn(n2)*sgn(d2);
|
||||
n2 = abs(n2); d2 = abs(d2);
|
||||
// quantise to smaller denominator to avoid increasing any numerator
|
||||
int64_t u = d1<d2? d1:d2;
|
||||
if (u < Time::SCALE)
|
||||
// regarding precision, quantising to µ-grid is the better solution
|
||||
u = Time::SCALE;
|
||||
else //re-quantise to common denominator more fine-grained than µ-grid
|
||||
if (s1*s2 > 0 // check numerators to detect danger of wrap-around
|
||||
and (62<util::ilog2(n1) or 62<util::ilog2(n2)))
|
||||
u >>= 1; // danger zone! wrap-around imminent
|
||||
|
||||
n1 = d1==u? n1 : reQuant (n1,d1, u);
|
||||
n2 = d2==u? n2 : reQuant (n2,d2, u);
|
||||
FSecs res{s1*n1 + s2*n2, u};
|
||||
ENSURE (abs (guess - approx(res)) < 1.0/u);
|
||||
return detox (res);
|
||||
}
|
||||
else
|
||||
// directly calculate ordinary numbers...
|
||||
return t1 + t2;
|
||||
}
|
||||
|
||||
|
||||
static Rat
|
||||
establishMetric (uint pxWidth, Time startWin, Time afterWin)
|
||||
|
|
@ -765,7 +819,7 @@ namespace model {
|
|||
Rat posFactor = canvasOffset / FSecs{afterAll_-startAll_};
|
||||
posFactor = parabolicAnchorRule (posFactor); // also limited 0...1
|
||||
FSecs partBeforeAnchor = scaleSafe (duration, posFactor);
|
||||
startWin_ = startAll_ + (canvasOffset - partBeforeAnchor);
|
||||
startWin_ = startAll_ + addSafe (canvasOffset, -partBeforeAnchor);
|
||||
establishWindowDuration (duration);
|
||||
startAll_ = min (startAll_, startWin_);
|
||||
afterAll_ = max (afterAll_, afterWin_);
|
||||
|
|
|
|||
|
|
@ -359,11 +359,12 @@ namespace test {
|
|||
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 (1/poison, MAX) == 1317624576693539413_r/9223372036854775807);
|
||||
CHECK (reQuant (-poison, 7777) == -54438_r/ 7777);
|
||||
CHECK (reQuant (poison, -7777) == -54438_r/-7777);
|
||||
|
||||
CHECK (approx (reQuant (poison, MAX-7)) == 7);
|
||||
CHECK (approx ( 1/poison ) == 0.142857149f);
|
||||
CHECK (approx (reQuant (1/poison, MAX)) == 0.142857149f);
|
||||
CHECK (approx (reQuant (poison, 7777)) == 6.99987125f);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -554,8 +554,10 @@ namespace test {
|
|||
CHECK (poison == 206435633551724850_r/307445734561825883);
|
||||
CHECK (2_r/3 < poison and poison < 1); // looks innocuous...
|
||||
CHECK (poison + Time::SCALE < 0); // simple calculations fail due to numeric overflow
|
||||
CHECK (Time(FSecs(poison)) < Time::ZERO); // conversion to µ-ticks also leads to overflow
|
||||
CHECK (-6 == _raw(Time(FSecs(poison))));
|
||||
CHECK (poison * Time::SCALE < 0);
|
||||
CHECK (-6 == rational_cast<gavl_time_t>(poison * Time::SCALE)); // naive conversion to µ-ticks would leads to overflow
|
||||
CHECK (671453 == _raw(Time(FSecs(poison)))); // however the actual conversion routine is safeguarded
|
||||
CHECK (671453.812f == rational_cast<float>(poison)*Time::SCALE);
|
||||
|
||||
using util::ilog2;
|
||||
CHECK (40 == ilog2 (LIM_HAZARD)); // LIM_HAZARD is based on MAX_INT / Time::Scale
|
||||
|
|
@ -624,7 +626,7 @@ namespace test {
|
|||
|
||||
Rat poisonousDuration = win.pxWidth() / poison; // Now, to demonstrate this "poison" was actually dangerous
|
||||
CHECK (poisonousDuration == 7071251894921995309_r/8257425342068994); // ...when we attempt to calculate the new duration directly....
|
||||
CHECK (Time(poisonousDuration) < Time::ZERO); // ...then a conversion to TimeValue will cause integer wrap
|
||||
CHECK (poisonousDuration * Time::SCALE < 0); // ...then a conversion to TimeValue will cause integer wrap
|
||||
CHECK(856.350708f == rational_cast<float> (poisonousDuration)); // yet numerically the duration actually established is almost the same
|
||||
CHECK(856.350708f == rational_cast<float> (_FSecs(win.visible().duration())));
|
||||
CHECK (win.px_per_sec() == 575000000_r/856350691); // the new metric however is comprised of sanitised fractional numbers
|
||||
|
|
@ -642,7 +644,8 @@ namespace test {
|
|||
SHOW_EXPR(win.px_per_sec());
|
||||
SHOW_EXPR(win.pxWidth());
|
||||
SHOW_EXPR(_raw(win.overallSpan().duration()) * rational_cast<double> (poison))
|
||||
Time targetPos{TimeValue{gavl_time_t(_raw(win.overallSpan().duration()) * rational_cast<double> (poison))}};
|
||||
TimeValue targetPos{gavl_time_t(_raw(win.overallSpan().duration())
|
||||
* rational_cast<double> (poison))};
|
||||
SHOW_EXPR(targetPos);
|
||||
SHOW_EXPR(_raw(targetPos));
|
||||
SHOW_EXPR(_raw(win.visible().start()))
|
||||
|
|
|
|||
|
|
@ -40386,6 +40386,122 @@
|
|||
<node COLOR="#338800" CREATED="1669682825470" ID="ID_906501375" MODIFIED="1669682838860" TEXT="verallgemeinert: scaleSafe()">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1669911724009" ID="ID_289279043" MODIFIED="1669911765633" TEXT="verallgemeinert: addSafe()">
|
||||
<linktarget COLOR="#a32f55" DESTINATION="ID_289279043" ENDARROW="Default" ENDINCLINATION="167;16;" ID="Arrow_ID_333407901" SOURCE="ID_797370792" STARTARROW="None" STARTINCLINATION="-174;-8;"/>
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1669932544504" ID="ID_750418137" MODIFIED="1669932569275" TEXT="FSecs ⟼ µ-Tick absichern">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node CREATED="1669932578600" ID="ID_974803158" MODIFIED="1669932591544" TEXT="das erweist sich als häufige Quelle von wrap-arounds">
|
||||
<icon BUILTIN="messagebox_warning"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1669932596081" ID="ID_1661291024" MODIFIED="1669932618249" TEXT="dabei ist es einfach bloß naiv gecodet">
|
||||
<icon BUILTIN="smiley-oh"/>
|
||||
</node>
|
||||
<node CREATED="1669932620086" ID="ID_673992117" MODIFIED="1669932631571" TEXT="stattdessen die re-Quantisierung nutzen">
|
||||
<icon BUILTIN="idea"/>
|
||||
<node CREATED="1669932632788" ID="ID_831065868" MODIFIED="1669932761462" TEXT="nur dann hier auf 1/1000000">
|
||||
<icon BUILTIN="yes"/>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1669932640820" ID="ID_1387350127" MODIFIED="1669932675887" TEXT="dazu eine Variante für den Zähler allein schaffen">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1669932653473" ID="ID_120882361" MODIFIED="1669932684292" TEXT="kann sogar den Rat-Fall hierauf zurückführen">
|
||||
<icon BUILTIN="idea"/>
|
||||
</node>
|
||||
<node COLOR="#435e98" CREATED="1669932686823" ID="ID_1630699394" MODIFIED="1669932696052" TEXT="Numerik-Probleme">
|
||||
<icon BUILTIN="broken-line"/>
|
||||
<node CREATED="1669932700323" ID="ID_1606021601" MODIFIED="1669932716796" TEXT="tja... Float bleibt Float selbst bei 128bit">
|
||||
<icon BUILTIN="clanbomber"/>
|
||||
</node>
|
||||
<node CREATED="1669932728399" ID="ID_20790978" MODIFIED="1669932757508">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
man <b>muß</b> Floating-point runden wenn man glatte Werte will
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
<icon BUILTIN="yes"/>
|
||||
</node>
|
||||
<node COLOR="#435e98" CREATED="1669932772210" ID="ID_787506618" MODIFIED="1669933074821" TEXT="Runden oder Tuncate?">
|
||||
<icon BUILTIN="help"/>
|
||||
<node CREATED="1669932795247" ID="ID_258240741" MODIFIED="1669932979085" TEXT="Zeit in Lumiera stets left-Truncate">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
das ist essentiell wichtig. "Negative" Zeiten dürfen sich <i>keinesfalls</i>  anders verhalten. Eine andere Quantsierungs-Regel kann man dann ggfs. high-level auf ein left-Truncate aufsetzen (z.B. Mitte Frame-Intervall)
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
<icon BUILTIN="yes"/>
|
||||
</node>
|
||||
<node CREATED="1669932981526" ID="ID_837170036" MODIFIED="1669933009189">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
⟹ also muß man hier <i>technisch runden</i>
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
</node>
|
||||
<node CREATED="1669933011434" ID="ID_1937730928" MODIFIED="1669933021860" TEXT="das bedeutet: auf 1/INT_MAX"/>
|
||||
<node CREATED="1669933027664" ID="ID_1390211698" MODIFIED="1669933038658" TEXT="weil ein Bruch hier interpretiert wird als Quantisierung"/>
|
||||
<node CREATED="1669933049926" ID="ID_1930108218" MODIFIED="1669933071839" TEXT="und wir daher auf die kleinstmöglich darstellbare rationale Zahl runden"/>
|
||||
</node>
|
||||
<node COLOR="#435e98" CREATED="1669933076257" ID="ID_68878490" MODIFIED="1669933203590" TEXT="geht das überhaupt?">
|
||||
<icon BUILTIN="help"/>
|
||||
<node CREATED="1669933097062" ID="ID_1298571080" MODIFIED="1669933101833" TEXT="mit double nicht...."/>
|
||||
<node CREATED="1669933102558" ID="ID_561202442" MODIFIED="1669933112105" TEXT="aber ich arbeite hier ja bewußt mit long double"/>
|
||||
<node CREATED="1669933113580" ID="ID_1052633426" MODIFIED="1669933130786" TEXT="aber selbst da sprengt ein absolutes ULP den Wertebereich"/>
|
||||
<node CREATED="1669933140405" ID="ID_672266484" MODIFIED="1669933163530" TEXT="⟹ also relative ULP nehmen: *(1+ULP)"/>
|
||||
<node CREATED="1669933167811" ID="ID_1558282411" MODIFIED="1669933183695" TEXT="das geht grade noch auf"/>
|
||||
<node CREATED="1669933185842" ID="ID_1427917386" MODIFIED="1669933196158" TEXT="Schwein gehabt">
|
||||
<icon BUILTIN="smiley-oh"/>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1669933204808" ID="ID_1033893690" MODIFIED="1669933252753" TEXT="WARNUNG: falls long double == double dann ist diese Rundung unwirksam">
|
||||
<icon BUILTIN="messagebox_warning"/>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1669933257385" ID="ID_1774777202" MODIFIED="1669933428563" TEXT="Testfall: TimeParsing_test">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node CREATED="1669933273591" ID="ID_1754745072" MODIFIED="1669933393067" TEXT="Darstellung von 1/250s">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
...das sind 4ms
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
</node>
|
||||
<node CREATED="1669933281232" ID="ID_542233825" MODIFIED="1669933418231" TEXT="ohne Rundung: 3999µs">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
parsing '1/250sec' resulted in 0:00:00.003 instead of 0:00:00.004
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1668707398858" ID="ID_1529383789" MODIFIED="1668707464339" TEXT="Test">
|
||||
|
|
@ -40434,6 +40550,100 @@
|
|||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1669683157298" ID="ID_710000990" MODIFIED="1669683199793" TEXT="Fenster liegt verdächtig weit daneben">
|
||||
<icon BUILTIN="stop-sign"/>
|
||||
</node>
|
||||
<node CREATED="1669911421774" ID="ID_1721803067" MODIFIED="1669911465062" TEXT="Umwandlung FSecs ⟼ µ-ticks entgleist">
|
||||
<icon BUILTIN="broken-line"/>
|
||||
<node CREATED="1669911515423" ID="ID_245485754" MODIFIED="1669911569313" TEXT="detox() vorher">
|
||||
<icon BUILTIN="stop-sign"/>
|
||||
</node>
|
||||
<node CREATED="1669911534853" ID="ID_1958976369" MODIFIED="1669911684935" TEXT="die Summe selber könnte auch wrappen">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
...tut sie zwar nicht in dem Beispiel hier, aber mit genügend krimineller Energie ließe sich ein valides Beispiel konstruieren, wobei
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
die Ziel-Position dann außerhalb des legalen Bereichs liegen würde
|
||||
</li>
|
||||
<li>
|
||||
bei korrekter Behandlung daher das Ergebnis-Fenster in den legalen Bereich geschoben werden müßte
|
||||
</li>
|
||||
<li>
|
||||
aber ohne weitere Schutzmaßname hier die Berechnung der Zielposition entgleist
|
||||
</li>
|
||||
</ul>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
<icon BUILTIN="clanbomber"/>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1669911687704" ID="ID_797370792" MODIFIED="1669911771424" TEXT="könnte man im nun etablierten Schema auch ein addSafe() definieren">
|
||||
<arrowlink COLOR="#a32f55" DESTINATION="ID_289279043" ENDARROW="Default" ENDINCLINATION="167;16;" ID="Arrow_ID_333407901" STARTARROW="None" STARTINCLINATION="-174;-8;"/>
|
||||
<icon BUILTIN="help"/>
|
||||
<node COLOR="#338800" CREATED="1669911787964" ID="ID_147783330" MODIFIED="1669911798248" TEXT="Erkennung und Behandlung der Grenzbedingungen analog">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1669911817888" ID="ID_493362427" MODIFIED="1669911841073" TEXT="Problem: Summe könnte trotzdem entgleisen">
|
||||
<icon BUILTIN="messagebox_warning"/>
|
||||
<node CREATED="1669912456275" ID="ID_661856327" MODIFIED="1669912464070" TEXT="nur bei Addition / gleicher Richtung"/>
|
||||
<node CREATED="1669912668224" ID="ID_1384499593" MODIFIED="1669914705545" TEXT="nur wenn bei mindestens einem Summanden das höchste Bit gesetzt ist">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
0111 + 0111 = 1110
|
||||
</p>
|
||||
<p>
|
||||
0011 + 0101 = 1000
|
||||
</p>
|
||||
<p>
|
||||
aber...
|
||||
</p>
|
||||
<p>
|
||||
0010 + 0101 = 0111
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
<linktarget COLOR="#644f94" DESTINATION="ID_1384499593" ENDARROW="Default" ENDINCLINATION="-195;8;" ID="Arrow_ID_899257928" SOURCE="ID_41244947" STARTARROW="None" STARTINCLINATION="-101;-8;"/>
|
||||
</node>
|
||||
<node CREATED="1669913417600" ID="ID_175910999" MODIFIED="1669913524753" TEXT="⟹ re-Quantisierung muß außerdem beide Summanden aus der Gefahrenzone bringen"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1669913560481" ID="ID_756192644" MODIFIED="1669913569368" TEXT="konkretes Rechenschema">
|
||||
<icon BUILTIN="info"/>
|
||||
<node CREATED="1669913571311" ID="ID_1313345585" MODIFIED="1669914699511" TEXT="verwende den (absolut) kleineren der beiden Nenner"/>
|
||||
<node CREATED="1669914430412" ID="ID_41244947" MODIFIED="1669914714600" TEXT="überprüfe die Gefahrenzone für die Zähler">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
Gefahr besteht...
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
wenn beide Vorzeichen gleichgerichtet sind
|
||||
</li>
|
||||
<li>
|
||||
wenn mindestens einer der Zähler das 63te Bit gesetzt hat (2^62)
|
||||
</li>
|
||||
</ul>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
<arrowlink COLOR="#644f94" DESTINATION="ID_1384499593" ENDARROW="Default" ENDINCLINATION="-195;8;" ID="Arrow_ID_899257928" STARTARROW="None" STARTINCLINATION="-101;-8;"/>
|
||||
<node CREATED="1669914775664" ID="ID_1885451601" MODIFIED="1669914823442" TEXT="wenn ungefährlich, dann nur größeren Nenner requantisieren"/>
|
||||
<node CREATED="1669914824383" ID="ID_999893302" MODIFIED="1669914845448" TEXT="wenn gefährlich, Nenner um ein Bit shiften und beide requantisieren"/>
|
||||
</node>
|
||||
<node CREATED="1669914866913" ID="ID_1877357496" MODIFIED="1669914878786" TEXT="Summation der angepaßten Zähler ausführen"/>
|
||||
<node CREATED="1669914880928" ID="ID_1472462771" MODIFIED="1669914895257" TEXT="Ergebnis mit gemeinsamem Nenner konstruieren"/>
|
||||
<node CREATED="1669914896208" ID="ID_370915623" MODIFIED="1669914901963" TEXT="Ergebnis entgiften"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
|
|
|
|||
Loading…
Reference in a new issue