Den Bruchteil eines Floats erhalten, ohne modf() zu verwenden
Lesezeit: 5 Minuten
Ich entwickle für eine Plattform ohne Mathematikbibliothek, also muss ich meine eigenen Tools bauen. Meine derzeitige Methode, den Bruch zu erhalten, besteht darin, den Float in einen Festkommawert umzuwandeln (mit (float)0xFFFF zu multiplizieren, in int umzuwandeln), nur den unteren Teil zu erhalten (mit 0xFFFF zu maskieren) und ihn wieder in einen Float umzuwandeln.
Allerdings bringt mich die Ungenauigkeit um. Ich verwende meine Frac()- und InvFrac()-Funktionen, um eine Anti-Aliasing-Linie zu zeichnen. Verwenden modf Ich bekomme eine perfekt glatte Linie. Bei meiner eigenen Methode springen Pixel aufgrund von Präzisionsverlusten herum.
Heiliger Strohsack. Mach es zu einer Antwort, damit ich es akzeptieren kann! Sie haben gerade mein gesamtes Genauigkeitsproblem behoben!
– Ritter666
7. April 2010 um 21:27 Uhr
Warum hat niemand den Originalcode bearbeitet, wenn der Kommentar ihn behoben hat?
– RufusVS
31. Oktober 2019 um 19:46 Uhr
Daniel Binham
Wenn ich deine Frage richtig verstehe, willst du nur die Nachkommastelle oder? Sie brauchen es eigentlich nicht in einem Bruch (ganzzahliger Zähler und Nenner)?
Wir haben also eine Zahl, sagen wir 3.14159 und wir wollen am Ende nur 0.14159. Angenommen, unsere Nummer ist in gespeichert float f;Wir können das schaffen:
f = f-(long)f;
Was, wenn wir unsere Nummer einfügen, so funktioniert:
0.14159 = 3.14159 - 3;
Dadurch wird der ganzzahlige Teil des Floats entfernt und nur der Dezimalteil übrig gelassen. Wenn Sie den Float in einen Long umwandeln, wird der Dezimalteil gelöscht. Wenn Sie das dann von Ihrem ursprünglichen Float abziehen, bleibt übrig nur der Dezimalteil. Wir müssen hier wegen der Größe eine lange verwenden float Typ (8 Bytes auf den meisten Systemen). Eine Ganzzahl (auf vielen Systemen nur 4 Bytes) ist nicht unbedingt groß genug, um den gleichen Zahlenbereich abzudecken wie a floataber ein long sollte sein.
Ein if… then… else… in einer mathematischen Funktion, die so oft verwendet wird wie diese? Mein Cache, es weint!
– Ritter666
7. April 2010 um 18:46 Uhr
Das ist wann falsch f ist negativ. (Sie addieren zwei negative Zahlen.) Sie brauchen die nicht if überhaupt: f = f - (int) f. Wenn f negativ ist, wird ein negatives int subtrahiert, das auf Null gerundet wird.
– jamesdlin
7. April 2010 um 18:52 Uhr
Außerdem gehen Sie davon aus, dass der ganzzahlige Teil des Floats in ein int passt.
– jamesdlin
7. April 2010 um 19:06 Uhr
Nun ja. Diese Antwort ist sowohl am einfachsten zu implementieren als auch am schnellsten (etwa 20 % schneller als meine Methode). Akzeptiert!
– Ritter666
9. April 2010 um 5:09 Uhr
(long)f; scheitert wann f ist viel außerhalb der long Angebot. (long long)f; erweitert die Reichweite, aber immer noch das gleiche Problem.
– chux – Wiedereinsetzung von Monica
24. Februar 2018 um 5:17 Uhr
AVB
Wie ich vermutete, modf verwendet keine Arithmetik an sich — es sind alles Schichten und Masken, schau mal hier. Können Sie nicht dieselben Ideen auf Ihrer Plattform verwenden?
Bill Lynch
Ich würde empfehlen, einen Blick darauf zu werfen, wie modf auf den Systemen implementiert ist, die Sie heute verwenden. Sehen Sie sich die Version von uClibc an.
Warum all die Bitverschiebungen, ist das wirklich ein Geschwindigkeitsgewinn? Oder hat mein kleiner int-Konvertierungstrick ein Problem, das ich übersehe?
– Daniel Binham
7. April 2010 um 18:41 Uhr
@Daniel Bingham: wahrscheinlich letzteres. Floats sind möglicherweise nicht so codiert, wie Sie auf der von Ihnen verwendeten Plattform denken, sodass Ihre Masken möglicherweise deaktiviert sind. @sharth: Ihr Link ist auf einige Makros angewiesen, und ich habe Probleme, sie zu finden. Können Sie Ihr Glück versuchen und Definitionen für EXTRACT_WORDS, INSERT_WORDS und GET_HIGH_WORD finden?
– Randolpho
7. April 2010 um 18:44 Uhr
Int zu schweben ist in Ordnung. Float to int ist furchtbar langsam.
– Ritter666
7. April 2010 um 18:45 Uhr
Es gibt einen Fehler in Ihren Konstanten. Sie versuchen im Grunde, die Zahl um 16 Bit nach links zu verschieben, alles außer den unteren Bits zu maskieren und dann erneut um 16 Bit nach rechts zu verschieben. Das Verschieben ist dasselbe wie das Multiplizieren mit einer Potenz von 2, aber Sie verwenden keine Potenz von 2 – Sie verwenden 0xFFFF, das um 1 ausgeschaltet ist. Wenn Sie dies durch 0x10000 ersetzen, funktioniert die Formel wie beabsichtigt.
Ich bin mir nicht ganz sicher, aber ich denke, dass das, was Sie tun, falsch ist, da Sie nur die Mantisse berücksichtigen und den Exponenten vollständig vergessen.
Sie müssen den Exponenten verwenden, um den Wert in der Mantisse zu verschieben, um den tatsächlichen ganzzahligen Teil zu finden.
Eine Beschreibung des Speichermechanismus von 32-Bit-Floats finden Sie hier hier.
Michael Dorgan
Warum sollten Sie für Ihre Strichzeichnung überhaupt zu Gleitkommazahlen wechseln? Sie könnten einfach bei Ihrer Festkommaversion bleiben und stattdessen eine auf Ganzzahlen/Festkomma basierende Strichzeichnungsroutine verwenden – Bresenhams kommt mir in den Sinn. Obwohl diese Version kein Alias ist, weiß ich, dass es andere gibt, die es sind.
Sollte fp_amount nicht 0x10000 statt 0xFFFF sein?
– Markieren Sie Lösegeld
7. April 2010 um 20:54 Uhr
Heiliger Strohsack. Mach es zu einer Antwort, damit ich es akzeptieren kann! Sie haben gerade mein gesamtes Genauigkeitsproblem behoben!
– Ritter666
7. April 2010 um 21:27 Uhr
Warum hat niemand den Originalcode bearbeitet, wenn der Kommentar ihn behoben hat?
– RufusVS
31. Oktober 2019 um 19:46 Uhr