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.

Das ist mein Code:

const float fp_amount = (float)(0xFFFF);
const float fp_amount_inv = 1.f / fp_amount;

inline float Frac(float a_X)
{
    return ((int)(a_X * fp_amount) & 0xFFFF) * fp_amount_inv;
}

inline float Frac(float a_X)
{
    return (0xFFFF - (int)(a_X * fp_amount) & 0xFFFF) * fp_amount_inv;
}

Danke im Voraus!

  • 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

Benutzer-Avatar
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

Benutzer-Avatar
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?

Benutzer-Avatar
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.

http://git.uclibc.org/uClibc/tree/libm/s_modf.c

(Aus rechtlichen Gründen scheint es BSD-lizenziert zu sein, aber Sie möchten es natürlich noch einmal überprüfen.)

Einige der Makros sind definiert hier.

  • 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.

Benutzer-Avatar
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.

Bresenhams Strichzeichnung

Benutzer-Avatar
Viktor Engel

Scheint so, als ob du das vielleicht willst.

float f = something;
float fractionalPart = f - floor(f);

  • floor ist auch langsamer als eine Besetzung.

    – aledalgrande

    14. August 2014 um 19:08 Uhr

1372500cookie-checkDen Bruchteil eines Floats erhalten, ohne modf() zu verwenden

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy