Frage

Natürlich sind die meisten Sprachen haben Bibliotheksfunktionen für diese, aber nehme ich will es selbst tun.

Nehmen wir an, dass der Schwimmer wie in einem C oder Java-Programm gegeben wird (mit Ausnahme der ‚f‘ oder ‚d‘ Suffix), zum Beispiel „4.2e1“, „.42e2“ oder einfach „42“. Im Allgemeinen haben wir den „Integer-Teil“ vor dem Komma, den „Bruchteil“ nach dem Komma, und den „Exponenten“. Alle drei ganze Zahlen sind.

Es ist leicht zu finden und die einzelnen Ziffern zu verarbeiten, sondern wie man sie in einen Wert vom Typ float oder double komponiert ohne Präzision zu verlieren?

Ich denke an der Multiplikation des Integer-Teils mit 10 ^ n , wobei n ist die Anzahl der Stellen in dem Bruchteil, und dann das Bruchteil zu der ganzzahlige Teil und Subtrahieren n vom Exponenten. Diese effektiv dreht 4.2e1 in 42e0, zum Beispiel. Dann könnte ich die pow Funktion verwenden, um 10 zu berechnen ^ Exponent und das Ergebnis mit dem neuen ganzzahligen Teil multipliziert. Die Frage ist, ob diese Methode garantiert höchste Präzision über?

Alle Gedanken auf, das?

War es hilfreich?

Lösung

Ich würde die Gleitkommazahl mit seiner binären Darstellung direkt montieren.

in der Zahl ein Zeichen nach dem anderen lesen und zunächst alle Ziffern finden. Tun Sie das in Integer-Arithmetik. Halten Sie auch den Überblick über das Komma und den Exponenten. Dieser wird später wichtig sein.

Jetzt können Sie Ihre Gleitkommazahl montieren. Das erste, was zu tun ist, die Ganzzahl-Darstellung der Ziffern für den ersten Satz ein Bit (höchste zu niedrigste) zu scannen.

Die Bits unmittelbar nach dem ersten Ein-Bit sind Ihre Mantisse.

die Exponenten zu erhalten ist entweder nicht hart. Sie kennen die erste Ein-Bit-Position, die Position des Dezimalpunkts und den optionalen Exponenten aus der wissenschaftlichen Notation. Kombinieren Sie sie und fügen Sie den Gleitkommaexponenten Bias (ich glaube, es ist 127, aber überprüfen Sie einige Referenz bitte).

Dieser Exponent soll irgendwo im Bereich von 0 bis 255 sein, wenn es größer oder kleiner Sie haben eine positive oder negative unendliche Zahl (Sonderfall).

Speichern Sie die Exponenten, wie es in die Bits 24 bis 30 des Schwimmers.

Das höchstwertige Bit ist einfach das Zeichen. Ein Mittel negativ, Null bedeutet, positiv.

Es ist schwieriger zu beschreiben, als es wirklich ist, versuchen, eine Gleitkommazahl zu zersetzen und einen Blick auf die Exponent und Mantisse nehmen und Sie werden sehen, wie einfach es wirklich ist.

Btw - das arithmetische tun Punkt in schwebenden selbst ist eine schlechte Idee, weil Sie immer Ihre Mantisse zu 23 Bits abgeschnitten werden zwingen wird. Sie werden keine genaue Darstellung auf diese Weise erhalten.

Andere Tipps

Alle anderen Antworten verpasst haben, wie hart ist dies richtig zu tun. Sie können dies einen ersten Schnitt Ansatz tun, die zu einem gewissen Grad genau ist, aber bis Sie berücksichtigen IEEE Modi Rundung (et al), werden Sie nie die rechts Antwort. Ich habe geschrieben naive Implementierungen, bevor sie mit einer ziemlich großen Menge Fehler.

Wenn Sie keine Angst vor Mathe sind, empfehle ich den folgenden Artikel von David Goldberg zu lesen, Was jeder Informatiker wissen sollten über Gleitkommaarithmetik . Sie werden ein besseres Verständnis für das, was unter der Haube vor sich geht, und warum die Bits als solche ausgelegt werden.

Mein bester Rat ist, mit einem Arbeits atoi Implementierung beginnen und von dort ausziehen. Sie werden schnell finden Sie die Dinge fehlt, aber ein paar Blicke auf strtod ‚s Quelle und Sie werden auf dem richtigen Weg sein (das ist ein langer, langer Weg ist). Irgendwann werden Sie Lob Insert diety hier , dass es Standardbibliotheken.

/* use this to start your atof implementation */

/* atoi - christopher.watford@gmail.com */
/* PUBLIC DOMAIN */
long atoi(const char *value) {
  unsigned long ival = 0, c, n = 1, i = 0, oval;
  for( ; c = value[i]; ++i) /* chomp leading spaces */
    if(!isspace(c)) break;
  if(c == '-' || c == '+') { /* chomp sign */
    n = (c != '-' ? n : -1);
    i++;
  }
  while(c = value[i++]) { /* parse number */
    if(!isdigit(c)) return 0;
    ival = (ival * 10) + (c - '0'); /* mult/accum */
    if((n > 0 && ival > LONG_MAX)
    || (n < 0 && ival > (LONG_MAX + 1UL))) {
      /* report overflow/underflow */
      errno = ERANGE;
      return (n > 0 ? LONG_MAX : LONG_MIN);
    }
  }
  return (n>0 ? (long)ival : -(long)ival);
}

Der „Standard“ Algorithmus, um eine Dezimalzahl zu der besten Gleitkommazahlen Näherung für die Umwandlung von Wie lesen Gleitkommazahlen genau , herunterladbare von hier . Beachten Sie, dass diese korrekt ganze Zahlen Präzision Mehr tun, zumindest einen gewissen Prozentsatz der Zeit erfordert, um Ecke Fälle zu behandeln.

Algorithmen für den anderen Weg gehen, den Druck der beste Dezimalzahl aus einer variabelen Zahl, sind in Burger und Dybvig des Druck Gleitkommazahlen schnell und genau , herunterladbare korrekt Binary-Dezimal und Dezimalstelle gerundet -binary Conversions für Algorithmen in beide Richtungen gehen.

Sie können das Dezimalsystem ignorieren, wenn (mit Ausnahme seiner Lage) Parsen. Sagen Sie den Eingang war: 156.7834e10 ... Diese leicht in die ganze Zahl 1.567.834 gefolgt von e10 analysiert werden könnten, die Sie dann auf e6 ändern würde, da die Dezimalzahl 4 Ziffern aus dem Ende des „Ziffer“ Teil des Schwimmers war.

Precision ist ein Problem. Sie müssen die IEEE-Spezifikation der Sprache überprüfen, die Sie verwenden. Wenn die Anzahl der Bits in der Mantisse (oder Fraktion), die größer ist als die Anzahl von Bits in der Integer-Typ ist, dann werden Sie möglicherweise verlieren Präzision, wenn jemand Typen in einer Reihe wie:

5123.123123e0 -. Umwandelt 5123123123 in unserem Verfahren, die nicht in einer Integer passen, aber die Bits für 5,123123123 können in der Mantisse des Schwimmers spec passen

Natürlich könnten Sie eine Methode verwenden, die jede Ziffer vor dem Komma nimmt, multipliziert die aktuelle Summe (in einem Float) von 10, dann die neue Ziffer hinzufügt. Für Ziffern nach dem Komma, multiplizieren Sie die Ziffer durch eine wachsende Leistung von 10 vor dem aktuellen Gesamt hinzufügen. Diese Methode scheint die Frage zu bitten, warum diese überhaupt tun, aber, wie es die Verwendung der Floating-Point-primitive erfordert, ohne die leicht verfügbar Parsen Bibliotheken.

Wie auch immer, viel Glück!

Ja können Sie die Konstruktion in Gleitkommaoperationen zersetzen solange sind diese Operationen EXACT , und Sie können eine Einzel letzter ungenauer Betrieb.

Leider Gleitkommaoperationen bald werden ungenau, wenn Sie Genauigkeit der Mantisse überschreiten, werden die Ergebnisse gerundet. Sobald eine Rundung „Fehler“ eingeführt wird, wird es in weiteren Operationen kumuliert werden ...
 Also, in der Regel, NO , können Sie nicht so naiv Algorithmus verwenden, um beliebige Dezimalstellen zu konvertieren, konnte dies zu einer falsch gerundeten Zahl führen, weg von mehreren ULP der richtigen, wie andere bereits gesagt, Ihnen .

ABER Mal sehen, wie weit wir gehen können:

Wenn Sie genau den Schwimmer wie folgt rekonstruieren:

if(biasedExponent >= 0)
    return integerMantissa * (10^biasedExponent);
else
    return integerMantissa / (10^(-biasedExponent));

besteht die Gefahr, Genauigkeit überschreitet sowohl beim Kumulieren des integerMantissa wenn es viele Stellen hat, und wenn 10 auf die Kraft des biasedExponent ...

Anhebung

Zum Glück, wenn zuerst zwei Operationen genau sind, dann können Sie einen endgültigen ungenauen Betrieb leisten * oder / dank IEEE Eigenschaften, wird das Ergebnis richtig gerundet werden.

Lassen Sie sich dies auf einfache Genauigkeit Schwimmer, die eine Genauigkeit von 24 Bit haben.

10^8 > 2^24 > 10^7

, dass mehrere von 2 Anbetracht dessen wird nur den Exponenten erhöhen und lassen Sie die Mantisse unverändert, wir nur mit Leistungen von 5 für Potenzierung von 10 zu tun haben:

5^11 > 2^24 > 5^10

Obwohl, können Sie 7 Ziffern der Präzision bei der integerMantissa leisten und eine biasedExponent zwischen -10 und 10.

In doppelter Genauigkeit, 53 Bits,

10^16 > 2^53 > 10^15
5^23 > 2^53 > 5^22

So können Sie 15 Dezimalstellen leisten können, und ein vorgespanntes Exponent zwischen -22 und 22.

Es liegt an Ihnen, ob Ihre Zahlen immer im richtigen Bereich fallen ... (Wenn Sie wirklich schwierig sind, könnten Sie ordnen Mantisse und Exponent zum Ausgleich durch Einfügen / Entfernen nachfolgende Nullen).

Sonst werden Sie haben einige erweiterte Präzision verwenden.
Wenn Ihre Sprache beliebige Genauigkeit ganze Zahlen liefert, dann ist es ein bisschen schwierig, es richtig zu machen, aber nicht so schwer, ich habe dies in Smalltalk und darüber gebloggt unter http://smallissimo.blogspot.fr/2011/09/clarifying-and-optimizing.html und http://smallissimo.blogspot.fr/2011/09/reviewing-fraction-asfloat.html

Beachten Sie, dass diese einfache und naive Implementierungen. Glücklicherweise ist libc mehr optimiert.

Mein erster Gedanke ist, den String in ein int64 Mantisse und einem int Dezimalexponenten nur die ersten 18 Stellen der Mantisse mit zu analysieren. Zum Beispiel 1.2345e-5 würde in 12345 und -9 analysiert werden. Dann würde ich halte die Mantisse mit 10 multipliziert und Dekrementieren des Exponenten Mantisse bis der 18 Ziffern lang war (> 56 Bit Genauigkeit). Dann würde ich die Dezimalexponenten sucht in einer Tabelle bis zu einem Faktor und binäre Exponenten zu finden, die verwendet werden können, um die Anzahl von dezimal zu konvertieren n * 10 ^ m auf binären p * 2 ^ q Form. Der Faktor würde eine andere int64 sein, damit ich die Mantisse durch Multiplizieren würde, so dass ich die oberen 64-Bits der resultierenden 128-Bit-Zahl erhalten wird. Diese int64 Mantisse können nur die notwendige Präzision und den 2 ^ q Exponenten verliert mit einem Schwimmer gegossen werden kann ohne Verlust an Präzision mit Multiplikation angewandt werden.

Ich würde erwarten, dass dies sehr genau zu sein und sehr schnell, aber Sie können auch die speziellen Zahlen NaN, -unendlich, -0,0 und Unendlichkeit zu handhaben. Ich habe nicht daran gedacht, über die denormalized Zahlen oder Rundung Modi.

, dass Sie den Standard IEEE 754, um für die richtige binäre Darstellung zu verstehen haben. Danach können Sie verwenden Float.intBitsToFloat oder Double.longBitsToDouble .

http://en.wikipedia.org/wiki/IEEE_754

Wenn Sie das präziseste Ergebnis möglich wollen, sollten Sie eine höhere interne Arbeitsgenauigkeit verwenden, und dann das Ergebnis auf die gewünschte Präzision downconvert. Wenn Sie nicht ein paar ULPs Fehler dagegen haben, dann können Sie nur wiederholt multiplizieren mit 10 nach Bedarf mit der gewünschten Genauigkeit. Ich würde die Funktion pow () vermeiden, da es ungenaue Ergebnisse für große Exponenten produzieren wird.

Es ist nicht möglich, jede beliebige Zeichenfolge, die eine Zahl in eine Doppel- oder Schwimmer zu konvertieren, ohne Präzision zu verlieren. Es gibt viele gebrochenen Zahlen, die genau in dezimaler dargestellt werden können (beispielsweise „0,1“), die nur in einem binären Schwimmers oder Doppel angenähert werden kann. Dies ist ähnlich wie der Bruch 1/3 kann nicht genau in dezimal dargestellt werden, können Sie nur 0,333333 schreiben ...

Wenn Sie eine Bibliotheksfunktion nicht direkt verwenden wollen, warum sie nicht an den Quellcode für die Bibliotheksfunktionen? Sie haben erwähnt, Java; die meisten JDKs Schiff mit Quellcode für die Klassenbibliotheken und so konnte man sehen, wie die java.lang.Double.parseDouble (String) Methode funktioniert. Natürlich etwas wie BigDecimal ist besser für Präzision steuern und Modi Runden, aber sie sagte, dass es einen Schwimmer sein muss oder verdoppeln.

eine Zustandsmaschine verwenden. Es ist ziemlich einfach zu tun, und funktioniert auch, wenn der Datenstrom unterbrochen (Sie nur den Staat und das Teilergebnis halten müssen). Sie können auch einen Parser-Generator verwenden (wenn Sie etwas komplexere tun).

Ich bin mit Endstation. Eine Zustandsmaschine ist der beste Weg, um diese Aufgabe zu erfüllen, da es viele dummen Wege sind ein Parser gebrochen werden kann. Ich arbeite an einem jetzt, denke ich, es vollständig ist und es hat glaube ich 13 Staaten.

Das Problem ist nicht trivial.

Ich bin eine Hardware-Ingenieur interessiert Entwerfen Floating-Point-Hardware. Ich bin auf meiner zweiten Umsetzung.

Ich fand das heute http://speleotrove.com/decimal/decarith.pdf

, die auf Seite 18 einige interessante Testfälle gibt.

Ja, ich habe Clinger den Artikel zu lesen, aber ein einfältig Hardware-Ingenieur zu sein, kann ich meine Meinung nicht vorgestellt um den Code zu erhalten. Der Verweis auf Steele-Algorithmus wie in Knuths Text asnwered war hilfreich für mich. Eingang und Ausgang ist problematisch.

Alle oben genannten Verweise auf verschiedene Artikel sind ausgezeichnet.

Ich habe noch hier nur noch um sich zu registrieren, aber wenn ich tun, vorausgesetzt, die Anmeldung nicht genommen wird, wird es Broh sein. (Broh-Punkt).

Clyde

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top