Umgang mit Rundungsfehler



  • Guten Morgen,

    analog zu mein EPSILON Thema habe ich nun ein anderes Thema. ich habe eine funktion welche den Schnittpunkt zweier Geraden berechnet.

    a1= 0d/0d
    a2= 100d/100d
    b1 = 0d(100d
    b2= 0.05d/0.05d
    
    BOOL intersect_line_with_line(point_t* a1, point_t* a2, point_t* b1, point_t* b2, point_t* res)
    {
    	point_t c;
    
    	/* calc the slopes */
    	FLOAT_t slopeA = lineSlope(a1, a2);   // => 1.0000000000000000
    	FLOAT_t slopeB = lineSlope(b1, b2); // =>  -1999.0000000000000
    
    	//HIER WIRD noch IsNAN etc. überpüft, zur veranschaulichung weggelassen
    
            //Debugger :  (slopeA - slopeB) => 2000.0000000000000
            //Debugger:  (slopeA * a1->x - slopeB * b1->x + b1->y - a1->y) => 100.00000000000000
    
    	c.x = (slopeA * a1->x - slopeB * b1->x + b1->y - a1->y) / (slopeA - slopeB);
    	c.y = slopeB * (c.x - b1->x) + b1->y;
    
             // Debugger c.y = -0.050000000000000003
    }
    

    Was passiert hier dass c.x statt -0.05 => -0.050000000000000003 rauskommt? ich sehe wohl das nicht mehr im werte Bereich!?

    wie kann ich sowas umgehen?

    EDIT:

    void rounding()
    {
        double a = 100.0;
        double b = 2000.0;
        double d = (a / b);
    }
    

    hier kommt auch als Ergebnis d 0.050000000000000003 statt 0.05 .. hm!?


  • Mod

    Gib mal 0.05 im Zweiersystem an. Merkst du was? (Vielleicht etwas gewohnter, aber gleiches Prinzip: Gib 1/3 im Zehnersystem an)



  • @SeppJ sagte in Umgang mit Rundungsfehler:

    Gib mal 0.05 im Zweiersystem an. Merkst du was? (Vielleicht etwas gewohnter, aber gleiches Prinzip: Gib 1/3 im Zehnersystem an)

    ja ich merke was ,aber wie darf ich das am sichersten runden?



  • @SoIntMan Schau mal auf https://www.h-schmidt.net/FloatConverter/IEEE754.html

    Das ist für float, gilt aber entsprechend auch für double.

    Dort kannst du 0.05 Eingeben und es ist 0.0500000007451
    Der nächst kleinere Wert wäre 0.0499999970198

    Dazwischen gibt es nichts.

    Aber auf 6 Stellen (von der 5 an) ist es 0.05



  • aha ok verstehe , und ich muss dann entscheiden ob er ab oder aufrunden soll ceil /floor ? oder gibt da ne magic function



  • @DirkB sagte in Umgang mit Rundungsfehler:

    Dort kannst du 0.05 Eingeben und es ist 0.0500000007451
    Der nächst kleinere Wert wäre 0.0499999970198

    bzw. wie kann ich jetzt auf das nächste fragment ab/azufrunden also exakt dann 0.05000?


  • Mod

    @SoIntMan sagte in Umgang mit Rundungsfehler:

    aha ok verstehe , und ich muss dann entscheiden ob er ab oder aufrunden soll ceil /floor ? oder gibt da ne magic function

    Es ist der nächste Wert zu dem, den du eingibst. Das kann sogar vom Datentyp abhängen, in welche Richtung das wirkt. 0.15 wird beispielsweise 0.1500000059604644775390625 als Float und 0.149999999999999994448884876874 als Double.

    Ich weiß nicht, was du hier tun willst oder ob du das Problem wirklich verstanden hast, wenn du denkst, es gäbe irgendetwas zu tun. Die Zahl 0.05 gibt es nicht (im Binärsystem)! Da kannst du nichts gegen tun, das geht mathematisch einfach nicht.

    Nochmal Dezimalsystem, da es gewohnter ist: Wenn ich 1/3 auf 5 Nachkommastellen genau schreibe, ist die beste Näherung 0.33333. Aber 0.33333 ist nicht 1/3. Was denkst du, wäre nun zu tun?

    PS: Für Fragen dieser Art nutze ich immer diese exzellente Seite: https://www.binaryconvert.com/
    Sonst denkt noch jemand, ich hätte die 0.15 im Kopf gerechnet



  • @DirkB sagte in Umgang mit Rundungsfehler:

    Dort kannst du 0.05 Eingeben und es ist 0.0500000007451
    Der nächst kleinere Wert wäre 0.0499999970198
    Dazwischen gibt es nichts.

    @SeppJ sagte in Umgang mit Rundungsfehler:

    @SoIntMan sagte in Umgang mit Rundungsfehler:

    aha ok verstehe , und ich muss dann entscheiden ob er ab oder aufrunden soll ceil /floor ? oder gibt da ne magic function

    Es ist der nächste Wert zu dem, den du eingibst. Das kann sogar vom Datentyp abhängen, in welche Richtung das wirkt. 0.15 wird beispielsweise 0.1500000059604644775390625 als Float und 0.149999999999999994448884876874 als Double.

    Ich weiß nicht, was du hier tun willst oder ob du das Problem wirklich verstanden hast, wenn du denkst, es gäbe irgendetwas zu tun. Die Zahl 0.05 gibt es nicht (im Binärsystem)! Da kannst du nichts gegen tun, das geht mathematisch einfach nicht.

    Nochmal Dezimalsystem, da es gewohnter ist: Wenn ich 1/3 auf 5 Nachkommastellen genau schreibe, ist die beste Näherung 0.33333. Aber 0.33333 ist nicht 1/3. Was denkst du, wäre nun zu tun?

    PS: Für Fragen dieser Art nutze ich immer diese exzellente Seite: https://www.binaryconvert.com/
    Sonst denkt noch jemand, ich hätte die 0.15 im Kopf gerechnet

    jetzt hat es klick gemacht;) mit de frakmentierung

    Das Problem ist, dass ich ein Ergebnis auf 0 <= t <= 1 prüfen möchte. und wen dann t = 1.00000000002 o.ä. herauskommt is es eben false, aber eigentlich sollte es true sein;)


  • Mod

    Nein, 1.00000000002 ist > 1. Punkt. Nichts weiter zu diskutieren. Beziehungsweise es gäbe zu diskutieren, was du da tust (oder besser: was du wirklich erreichen möchtest), dass du denkst, du bräuchtest das anders. Denn es sind gewiss komische Anforderungen (oder ein Missverständnis), dass zu einer Anforderung führt, bei der 1.00000000002 <= 1 sein soll.

    Leite ich mir das richtig her: Du möchtest gucken, ob/wo zwei Geraden sich schneiden, aber hast nun Angst, dass die Geraden sich nicht/anderswo schneiden, als du denkst? Aber die Rechnung (sofern korrekt programmiert) wäre ja richtig: Wenn deine Gerade eine Steigung 0.1000000000021 hat statt 0.1, dann ist das Ergebnis halt ein anderes als du denkst, aber dieses Ergebnis ist trotzdem richtig (für 0.1000000000021). Das heißt, entweder passen deine Erwartungen nicht, oder deine Anforderungen sind andere und du hast den falschen Weg gewählt. Wenn deine Anforderung war "es soll das exakte Ergebnis für alle Werte rauskommen, die man im Dezimalsystem tippen kann", dann passt deine Umsetzung nicht dazu. Dann können wir darüber reden, wie man die besser/korrekt umsetzen kann (aber erwarte nicht, dass so etwas einfach wird!)



  • @SeppJ sagte in Umgang mit Rundungsfehler:

    Nein, 1.00000000002 ist > 1. Punkt. Nichts weiter zu diskutieren. Beziehungsweise es gäbe zu diskutieren, was du da tust (oder besser: was du wirklich erreichen möchtest), dass du denkst, du bräuchtest das anders. Denn es sind gewiss komische Anforderungen (oder ein Missverständnis), dass zu einer Anforderung führt, bei der 1.00000000002 <= 1 sein soll.

    Also gut , ich habe eine weitere Funktion welche den Schnittpunkt zweier"Strecken" berechnet:

    a = 0/0
    b = 100/100
    c = 0/200
    d = 0,0001/0,0001

    BOOL intersect_line_with_line2(point_t* A, point_t* B, point_t* C, point_t* D, point_t* out)
    {
    	// float s1_x, s1_y, s2_x, s2_y;
    	FLOAT_t s1_x = B->x - A->x;
    	FLOAT_t s1_y = B->y - A->y;
    	FLOAT_t s2_x = D->x - C->x;
    	FLOAT_t s2_y = D->y - C->y;
    
    	FLOAT_t d = (-s2_x * s1_y + s1_x * s2_y);
    
    	/* if 0 == parallel */
    //	if (!compare(d, 0))
    	if (!isnan(d))
    	{
    		/* line parameter */
    		FLOAT_t s = (-s1_y * (A->x - C->x) + s1_x * (A->y - C->y)) / d;
    		FLOAT_t t = (s2_x * (A->y - C->y) - s2_y * (A->x - C->x)) / d;
    
    // 
    		if (s >= 0.0 && s <= 1.0 && t >= 0.0 && t <= 1.0)
    		{
    			/* Collision detected */
    			out->x = A->x + (t * s1_x);
    			out->y = A->y + (t * s1_y);
    			return TRUE;
    		}
    	}
    
    	out->x = 0;
    	out->y = 0;
    	return FALSE; // No collision
    }
    

    und die strecken müssen sich definit schneiden, aber s ist hier 1.0000000000000002! das is das problem;)


  • Mod

    Ich habe oben noch eine Rückfrage zu den genauen Anforderungen reineditiert, wo mich deine Antwort interessieren würde.

    Problem hier ist halt (ich prüfe jetzt nicht deine Rechnung, ob die wirklich stimmt), dass (float) 0,0001 nicht das ist, was du denkst, und sich deine Strecken halt mathematisch nicht schneiden, mit dem Wert, der 0,0001 in Wirklichkeit ist. Da ist halt jetzt die Frage, was du wirklich genau willst, siehe meinen Edit oben zu den genauen Anforderungen.


  • Mod

    PS: Da ist jetzt auch wirklich wichtig, dass du nicht einfach blind Anforderungen formulierst, die möglichst genau klingen. Denk darüber nach, warum du etwas brauchst und was du damit machst. Wenn du das Ergebnis beispielsweise in einem Programm benutzt, das auch in Flieskommazahlen rechnet und wissen muss, ob sich zwei solche Strecken schneiden, dann ist das derzeit ja schon richtig; die Strecken schneiden sich nicht (vorausgesetzt deine Rechnung stimmt, was ich nicht geprüft habe) mit diesen Fließkommazahlen als Eingabe und es ist völlig egal, dass es in der Nähe andere Werte gäbe, bei denen sie sich schneiden. Wenn du ein CAS schreibst, ist es hingegen tatsächlich nicht korrekt, aber die genauen Anforderungen an ein CAS sind halt auch nicht so einfach zu formulieren.



  • @SoIntMan Das Literal 1.0 ist in C erstmal ein double.
    Wenn du float möchtest, schreib 1.0f

    Das ist die 1, die float hergibt.



  • @SeppJ sagte in Umgang mit Rundungsfehler:

    PS: Da ist jetzt auch wirklich wichtig, dass du nicht einfach blind Anforderungen formulierst, die möglichst genau klingen. Denk darüber nach, warum du etwas brauchst und was du damit machst. Wenn du das Ergebnis beispielsweise in einem Programm benutzt, das auch in Flieskommazahlen rechnet und wissen muss, ob sich zwei solche Strecken schneiden, dann ist das derzeit ja schon richtig; die Strecken schneiden sich nicht (vorausgesetzt deine Rechnung stimmt, was ich nicht geprüft habe) mit diesen Fließkommazahlen als Eingabe und es ist völlig egal, dass es in der Nähe andere Werte gäbe, bei denen sie sich schneiden. Wenn du ein CAS schreibst, ist es hingegen tatsächlich nicht korrekt, aber die genauen Anforderungen an ein CAS sind halt auch nicht so einfach zu formulieren.

    Guten Morgen Sepp,

    nun allein durch die Erkenntnis welche ich hier und bei meinem Vorhaben erlangt habe, und ich von der Kopf-Unendliche Genauigkeit weg muss. Wären meine Anforderungen für die wie folgt:

    Die Punkte der Linen/Geraden sollen frei im Raster 0,00001 gesetzt werden können, in einem Kartesischen Raum -1.000.000 <= (x/y) <= 1.000.000.

    @DirkB sagte in Umgang mit Rundungsfehler:

    @SoIntMan Das Literal 1.0 ist in C erstmal ein double.
    Wenn du float möchtest, schreib 1.0f
    Das ist die 1, die float hergibt.

    Danke dir , hinter dem FLOAT_t verbirgt sich ein double;)


  • Mod

    @SoIntMan sagte in Umgang mit Rundungsfehler:

    Die Punkte der Linen/Geraden sollen frei im Raster 0,00001 gesetzt werden können, in einem Kartesischen Raum -1.000.000 <= (x/y) <= 1.000.000.

    Hast du da jetzt wirklich drüber nachgedacht? Denn es sieht aus wie fix ausgedacht. Hast du wirklich einen Grund für 0.00001 statt 0.00001525878? (Die Zahl 0.00001525878 ist kein Zufall und solltest du wiedererkennen, wenn du wirklich nachgedacht hast). Warum 1,000,000 statt beispielsweise 1,048,576? Hast du einen Grund für das fixe Raster? Hast du über die Definitionen von Strecken und Schnittpunkten auf einem Raster nachgedacht?



  • @SeppJ sagte in Umgang mit Rundungsfehler:

    Hast du da jetzt wirklich drüber nachgedacht? Denn es sieht aus wie fix ausgedacht. Hast du wirklich einen Grund für 0.00001 statt 0.00001525878? (Die Zahl 0.00001525878 ist kein Zufall und solltest du wiedererkennen, wenn du wirklich nachgedacht hast). Warum 1,000,000 statt beispielsweise 1,048,576? Hast du einen Grund für das fixe Raster? Hast du über die Definitionen von Strecken und Schnittpunkten auf einem Raster nachgedacht?

    Du hast mich ertappt;) nein so genau habe ich nicht nachdacht. Stellt dir vor du hast ein Polygon mit N-Vertices. Welche willkürliche punkte haben können, aber da diese sich ja auch im selben Wertesystem befinden und ich bspw. random die punkte willkürlich bestimmte, können diese punkte jauch nicht 0.00001 sein sonder 0.00001525878 !? oder?

    Wie gehe ich da nun vor?



  • @SeppJ sagte in Umgang mit Rundungsfehler:

    Warum 1,000,000 statt beispielsweise 1,048,576?

    ja eigentich is das sogar + - beliebigt.. aber gröér kleiner ~1.000.000 past

    @SeppJ sagte in Umgang mit Rundungsfehler:

    Hast du einen Grund für das fixe Raster?

    naja da ganze ist für eine Simulation welche 0,00001 mm genau sein soll...


  • Mod

    @SoIntMan sagte in Umgang mit Rundungsfehler:

    Stellt dir vor du hast ein Polygon mit N-Vertices.
    […]
    naja da ganze ist für eine Simulation welche 0,00001 mm genau sein soll...

    Jetzt kommen wir langsam irgendwohin. Das klingt mal so langsam nach einer echten Anforderung. Erklär mal mehr!

    Wenn deine Genauigkeit 0.00001 sein soll, sollte deine Rechnung meistens feiner sein, kommt aber drauf an. Verdächtig ist aber auch, dass du andererseits die Obergrenze 1,000,000 nennst. Das sind 11 Größenordnungen. Die genaueste Messungen, die die Menschheit bisher jemals durchgeführt hat, sind auf ca 13-15 Stellen genau. Arbeitest du wirklich in so einem Bereich, oder ist das auch wieder nur willkürlich aus der Luft gezogen?



  • @SeppJ sagte in Umgang mit Rundungsfehler:

    Jetzt kommen wir langsam irgendwohin. Das klingt mal so langsam nach einer echten Anforderung. Erklär mal mehr!
    Wenn deine Genauigkeit 0.00001 sein soll, sollte deine Rechnung meistens feiner sein, kommt aber drauf an. Verdächtig ist aber auch, dass du andererseits die Obergrenze 1,000,000 nennst. Das sind 11 Größenordnungen. Die genaueste Messungen, die die Menschheit bisher jemals durchgeführt hat, sind auf ca 13-15 Stellen genau. Arbeitest du wirklich in so einem Bereich, oder ist das auch wieder nur willkürlich aus der Luft gezogen?

    Hey, ok dann lass die Obergrenze einfach weg, ist erstmal auch nicht wichtig (willkürlich). Anhand von einem Formel/Funktion wird ein Polygon berechnet. eine art Arc.segment bestehend aus n Line Segmenten.. ich denke durch Sin/cos kommen dann auch ganz wilde zahlen raus. Ich kann die Ergebnisse dann auch runden, das wäre natürlich hein ansatz, So ganz tief habe ich das noch nicht evaluiert, aber auch 0,001 wäre noch ok. muss ich noch klären;)

    Aber jetzt hab ich sogar noch eine weiter frage. ich habe eine Slope funktion welche mir die Steigung eine line zwischen zwei punkten berchenet:

    double slope = (a->y - b->y) / (a->x - b->x);
    

    wenn ich nun 2 punkte paare einsetze

    A: 300/400 B:300/300 => Slope -0
    A: 300/400 B:300/600 => Slope +0

    Wenn ich die beiden slopes vergleiche kommt true raus, aber eigenlich sollte es falsch sein wegen dem vorzeichen;) dann hab ich auf das erste bit geschaut:

    BOOL X1 = (BOOL)((int32_t)slopeA & 0x80000000);	
    BOOL X2 = (BOOL)((int32_t)slopeB & 0x80000000);
    BOOL X3 = (BOOL)((int32_t)slopeA & 0x1);
    BOOL X4 = (BOOL)((int32_t)slopeB & 0x1);
    

    aber hmm immer 0 !? zu blauäugig? bekomme ich das überhaupt raus?


  • Mod

    Aber wozu ist das alles gut? Ist das eine Simulation wo Polygone durch die Gegend fliegen und du willst ausrechnen, ob sie sich stoßen?

    Wenn ich die beiden slopes vergleiche kommt true raus, aber eigenlich sollte es falsch sein wegen dem vorzeichen;)

    Wieso soll 0 nicht 0 sein?


Anmelden zum Antworten