Modulo falsch Berechnung?
-
@wob ja wie Du sagst es ist tatsächlich so, ich bin davon ausgegangen.
So habe es mal durchprobiert mit verschiedenen Werten 32bit/64bit
double d=90000.024; double a=d/25*1000; a = std::floor(a + 0.001); int r = static_cast<int>(a)%1000; //richtig 0 cout << r << endl; d=90000.025; a=d/25*1000; a = std::floor(a + 0.001); r = static_cast<int>(a)%1000; //richtig 1 cout << r << endl; d=90000.049; a=d/25*1000; a = std::floor(a + 0.001); r = static_cast<int>(a)%1000; //richtig 1 cout << r << endl; d=90000.050; a=d/25*1000; a = std::floor(a + 0.001); r = static_cast<int>(a)%1000; //richtig 2 cout << r << endl; d=90000.074; a=d/25*1000; a = std::floor(a + 0.001); r = static_cast<int>(a)%1000; //richtig 2 cout << r << endl; d=90000.075; a=d/25*1000; a = std::floor(a + 0.001); r = static_cast<int>(a)%1000; //richtig 3 cout << r << endl;
std::floor(a + 0.00001) damit geht es auch, brauch ich sowieso für 5 Stellig hinter Komma bei nicht PAL
-
Willkommen in der Welt der Gleitkomma-Zahlen! Dein Problem ist dass du im Endeffekt ein
double
nachint
casten und hierbei alle Nachkommastellen abschneiden möchtest.Im Testfall 90000.025 ist der Wert
d/25*1000
bei mir jedoch 3600000.9999999995, sollte aber 3600001.0 sein.Also habe ich den cast etwas verändert. Ich habe die Zahl auf die 5. Nachkommastelle gerundet und dann alle Nachkommastellen abgeschnitten.
#include <cstdio> #include <cmath> #include <cassert> int Foo(double d) { static constexpr unsigned int n = 5; double Factor = std::pow(10.0, 5); double a = d / 25 * 1000; //int ai = (int)(a); int ai = static_cast<int>(std::floor(a * Factor + 0.5) / Factor); int e = ai % 1000; return e; } int main() { int e; e = Foo(90000.049); assert(e == 1); e = Foo(90000.050); assert(e == 2); e = Foo(90000.025); assert(e == 1); e = Foo(90000.024); assert(e == 0); return 0; }
BTW: Der Code würde ich in eine Testreihe packen.
-
@wob So hab das jetzt mal für NTSC mal durch 32/64bit, geht super:
//NTSC d=7191.04794; a=d/23.97*1000; a = std::floor(a + 0.00001); r = static_cast<int>(a)%1000; //richtig 2 cout << r << endl; //NTSC d=7191.04793; a=d/23.97*1000; a = std::floor(a + 0.00001); r = static_cast<int>(a)%1000; //richtig 1 cout << r << endl;
Danke Dir und an alle anderen natürlich auch. Ach was ich noch vergessen habe die ganze Zeit zu erwähnen die Ergebnisse spiegeln lediglich die millisekunden wieder, falls welche vorhanden sind.
-
@Quiche-Lorraine sagte in Modulo falsch Berechnung?:
#include <cassert>
so hab das mal durchprobiert für NTSC , scheint auch damit zu funktionieren , ich denke aber das ich den Code std::floor(a + 0.00001); nehmen werde, sieht einfacher aus.
-
Ich habe mir auch Deinen Code angeschaut. Das Hauptproblem in Deinem Code ist, dass Du nicht verstehst, dass sich binäre Zahlen nicht immer auf dezimale Zahlen und umgekehrt abbilden lassen. Ich habe dazu eine Routine geschrieben, die das Binäre IEEE754 Double Precision Format Binär dekodiert und ausgibt. (Negative Exponenten werden noch nicht dekodiert sondern direkt ausgegeben.) Daran kannst Du besser erkennen, weshalb Du hier Probleme hast.
#include <iostream> #include <iomanip> #include <cmath> #include <bitset> #include <string> using std::setprecision, std::setw, std::fixed; constexpr int w = 10; constexpr int p = 5; using DT = double; using IT = int; void print_double(const double d) { const char* const info[5] = {" denormalized", " normalized", " infinity", " NaN", " denormalized zero"}; const char* const lead[2] = {"1.", "0."}; char sign[2] = {'+', '-'}; uint64_t* u64 = (uint64_t*)&d; uint64_t mantissa = 0x000FFFFFFFFFFFFF & *u64; uint64_t exponent = 0x7FF0000000000000 & *u64; uint64_t sign64 = 0x8000000000000000 & *u64; sign64 = sign64 >> 63; exponent = exponent >> 52; size_t index = 0, lead_index = 0; if ((0 == exponent) && (0 == mantissa)) { index = 4; lead_index = 1; } else if ((0 == exponent) && (0 < mantissa)) { index = 0; lead_index = 1; exponent = 1 - exponent; } else if ((0 < exponent) && (2047 > exponent)) { index = 1; lead_index = 0; exponent -= 1023; } else if ((2047 == exponent) && (0 == mantissa)) { index = 2; } else if ((2047 == exponent) && (0 < mantissa)) { index = 3; } std::bitset<52> bmantissa(mantissa); std::bitset<11> bexponent(exponent); std::cout << setw(w) << setprecision(p) << d << ", " << sign[sign64] << lead[lead_index] << bmantissa << " ×2^ " << bexponent << info[index] << "\n"; } template<typename T1 = DT, typename T2 = IT> void calculate_and_print (const T1 d, const T2 correct_result) { T1 a = (d/25) * 1000; T2 i = round(a); T2 e = i%1000; uint64_t* pointer = (uint64_t*)&a; uint64_t mantissa = 0x000FFFFF & (*pointer); uint64_t exponent = 0x7FF00000 & (*pointer); exponent = exponent >> 52; std::cout << fixed; std::cout << correct_result << ", " << e << ", " << i << ", " << setw(w) << setprecision(p) << d << "\n"; print_double(a); std::cout << "\n"; } int main() { std::cout << "test output of some numbers\n"; print_double( 0.0); print_double( 0.125); print_double( 1.0); std::cout << "\n"; calculate_and_print(90000.049, 1); calculate_and_print(90000.050, 2); calculate_and_print(90000.025, 1); calculate_and_print(90000.024, 0); }
-
@Gestalt sagte in Modulo falsch Berechnung?:
7191.04794
Ich check grad nicht was du erreichen willst. Willst du ausrechnen wie lange 7191.04794 Frames eines NTSC Videos brauchen? Und von der Zeit dann sozusagen den Nachkommaanteil in Millisekunden? Verstehe nicht wozu das gut sein soll.
-
Um die Probleme zu umgehen geht auch folgendes, einfach die Werte bisschen erhöhen dann klappts auch ohne Werte dazu addieren, runden oder sonstiges.
double A = 90000.024*10; double B = 0.025*10; double C = (A/B); int R = static_cast<int>(C)%1000; //0 cout << R <<endl; A = 90000.025*10; B = 0.025*10; C = (A/B); //3600001 R = static_cast<int>(C)%1000; //1 cout << R <<endl; if(C==3600001) cout << "true" <<endl;
is schon spät muss erstmal pennen
Update: ist Schrott funktioniert nicht zuverlässig.
-
@Gestalt sagte in Modulo falsch Berechnung?:
double B = 0.025*10;
Naja, 0.25 kann exakt in double oder float dargestellt werden, 0.025 aber nicht. 0.24 übrigens nicht. Deine Berechnung funktioniert also nur "zufällig", du solltest bei Fließkommazahlen IMMER mit Rundungsfehlern/Darstellungsproblemen rechnen.
Dein erster Fall funktioniert, weil 3600000.96 (bzw. ...959999... in double) rauskommt, was sowieso in der 2. Nachkommastelle klein ist, d.h. 0.96 ist weit genug von der 1 entfernt, sodass du kein Problem hast.
Der 2. Fall ist spannender. Er funktioniert, weil 0.25 exakt ist.
Mit nicht exakt darstellbaren Nachkommazahlen, die mathematisch gesehen aber in deiner Rechnung eine ganze Zahl ergeben, wirst du Probleme haben, weil das Ergebnis dann auch geringfügig größer (kein Problem für dich) oder kleiner (da hast du dann ein Problem) als die ganze Zahl sein kann.
-
@wob sagte in Modulo falsch Berechnung?:
stellungsproblemen rec
habe zur Not noch Deine Variante die ja noch im Code steht, ich probiere das aber später nochmal durch bis es kracht :). Nur im Moment bastel ich an anderen Dingen, die mich 4 Stunden ehr ausm Bett geholt haben als eigentlich geplant.
Update: es bleibt dabei "std::floor(a + 0.00001);" beste Variante.
-
Tipp: Wenn es
float
,double
, usw. sein soll ... und die Ergebnisse genau sein sollen ... dann immer die C++-Boost-Library verwenden. Die Jungs machen quasi nix anderes.Und hier https://en.wikipedia.org/wiki/Accuracy_and_precision und hier https://en.wikipedia.org/wiki/Propagation_of_uncertainty steht mehr darüber.
Edit: Eigentlich wollte ich das in die Signatur schreiben, aber das darf ich nicht:
Menschen machen Fehler, aber Computer machen noch mehr Fehler.
-
@hustbaer sagte in Modulo falsch Berechnung?:
@Gestalt sagte in Modulo falsch Berechnung?:
7191.04794
Ich check grad nicht was du erreichen willst. Willst du ausrechnen wie lange 7191.04794 Frames eines NTSC Videos brauchen? Und von der Zeit dann sozusagen den Nachkommaanteil in Millisekunden? Verstehe nicht wozu das gut sein soll.
Das würde mich auch interessieren. Ich habe das Gefühl, dass man hier möglicherweise schon früher einen anderen Ansatz wählen kann, um Rundungsfehler zu minimieren. Soweit ich weiß sind diese PAL/NTSC Frameraten und Frequenzen auch alles Rationale Zahlen, da könnte man sogar weitgehend auf Fließkomma-Berechnungen verzichten, wenn man es wirklich exakt haben will - bzw. erst ganz am Ende das Ergebnis nach
double
umrechnen. So akkumulieren sich die Fehler in den Rechenschritten dann auch nicht auf.NTSC macht so weit ich weiß Bilder pro Sekunde, bzw. Sekunden pro Bild. Für 7191.04794 Frames (falls das hier überhaupt berechnet werden soll (?)) wären das dann Sekunden (exakt!).
Das nur als Beispiel, wie man Berechnungen wie diese mit Integer-Arithmetik in Zähler und Nenner machen und erst ganz am Schluss nach Fließkomma konvertieren könnte. Ich würde vermuten, dass sich damit einige Rundungs-Scherereien vermeiden ließen. Wäre trotzdem gut zu wissen, was du überhaupt berechnen willst @Gestalt - vielleicht ist das ja ein XY-Problem (?).
-
@Finnegan sagte in Modulo falsch Berechnung?:
7191.04794
ja für mich ist es sinnvoll sonst würde ich es nicht machen und Zeit in ein Programm stecken. In Deiner Berechnung nutzt Du das falsche Format für den Wert "7191.04794", diesem Wert liegt NTSC-FILM 23,97fps zugrunde und nicht 29,97fps. Diese "7191.04794" sind genau und zwar exakt nocht exakter geht es nicht 05 Stunden 00 Minuten 00 sekunden und 002 Millisekunden. Mein Programm funktioniert ich habe auch keine Rundungsfehler auch nicht bei 999 Stunden 59 Minuten 59 Sekunden 999Millisekunden das sind Exakt und ganz genau 86291999.97603 Frames im 23,97 NTSC FILM Format. Ich finde das nicht gut das man nach dem Sinn gefragt wird nur weil ich Dinge mache die eine anderer nicht macht und er deshalb darin keinen Sinn sieht.
-
Trotzdem benutzt man dafür dann aber keine Fließkommazahlen!
-
@Th69 Ok, dann Beweise mir das in dem Du "5006398.17" im NTSC 23,97 umrechnest zu Stunden/Minuten/Sekunden/Millisekunden ohne Fließkomma-Datentypen zu benutzen. Das Ergebniss reicht vorerst aus.
-
So, wie schon @Finnegan geschrieben hat, mit rationalen Zahlen.
Laut NTSC-Film entspricht dies (bzw. ), d.h.const int64_t ms_per_s = 1000; int64_t time_in_milliseconds = frames * ms_per_s * 1001 / 24000;
frames
sollte dabei aber auch schon eine rationale Zahl sein (bzw. in so eine umgewandelt werden können) - darum hatte ich dich ja auch schon gefragt woher der Wert fürd
(alsoframes
, wie wir jetzt wissen) kommt?
Du könntest dafür z.B. die Boost-Library Rational Numbers benutzen.Für
frames = 5006398.17
(bzw.frames = Rational(500639817, 100)
) sollte (wenn der Windows-Rechner sich nicht verrechnet hat) abgerundettime_in_milliseconds = 208808523
(bzw.58h 0m 8s 523ms
herauskommen).
-
@Th69 sagte in Modulo falsch Berechnung?:
int64_t
ja das Problem ist auch noch das es im NTSC-FILM 2 verschiedene Formate gibt einmal 24.0fps und einmal 23,976fps - das muss erstmal unterschieden werden. Deshalb ist alles ersteinmal hinfällig was dieses Format betrifft. Das heißt 23,97 ist schon falsch von dem ich ausgegangen bin. Dein Beispiel ist gut ich guck mir das mal genauer an. Aber ich werde sehr wahrscheinlich mit diesem 23,976fps und nicht mit (24000/1001) und dann zusätzlich halt 24.00fps was ja einfach ist arbeiten.
So hab erstmal auf 23,976 umgeschrieben, selber hatte ich damit nie zu tun nur eben mit PAL-Zeiten deshalb kann ich es nicht überprüfen. Herauskommen sollte beim Ergebniss eigentlich 58:01:01:000 - wenn 23,976fps zugrunde liegen. Der Frame-Wert dafür liegt bei 5007651.336.
NTSC 29,97 / NTSC FILM 23,976 = 1.25.
6259564.17/ 5007651.336 = 1.25. (TimeCode 58:01:01:000)Update: Habe mir mal Deine Berechnung reingezogen, irgendwas stimmt da nicht.
Komme damit nicht klar die Ergebnisse sind komplett daneben.
-
Da du anscheinend das RAD Studio (
AnsiString
undShowMessage
), also den Embarcadero-Compiler, benutzt, könntest du auch den Datentyp System.Currency verwenden - dieser hat exakt 4 Nachkommastellen (ist dem Namen nach eigentlich für Währungen gedacht, aber auch für andere Anwendungsfälle, wie deinem, geeignet).
-
@Th69 also bei NTSC 23.976 benötige ich maximal 6 Nachkommastellen.
86313599.976024 = 999:59:59:999 h/m/s/msDafür reicht dann aber int nicht mehr aus.
-
Ich schrieb ja auch
int64_t
.Und für Fixpoint-Arithmetics habe ich auch noch Compositional Numeric Library (CNL) gefunden (dort kannst du dann die Anzahl der max. Nachkommastellen explizit angeben).
Auch wenn ich nicht verstehe, warum du bei Millisekunden-Genauigkeit mehr als 3 Nachkommastellen benötigst.
-
@Th69 Es geht auch mit 3 nur muss ich dann aufrunden.