2012-06-05 9 views
19

Ich schreibe ein Programm, das Fließkomma-Literale druckt, die in einem anderen Programm verwendet werden sollen.float drucken, Präzision erhalten

Wie viele Ziffern muss ich drucken, um die Genauigkeit des ursprünglichen Schwimmers zu erhalten?

Da ein float 24 * (log(2)/log(10)) = 7.2247199 Dezimalstellen der Genauigkeit hat, war mein erster Gedanke, dass Drucken von 8 Ziffern sollte ausreichen. Aber wenn ich Pech habe, werden diese 0.2247199 nach links und rechts von den 7 signifikanten Stellen verteilt, also sollte ich wahrscheinlich 9 Dezimalziffern drucken.

Ist meine Analyse korrekt? Sind 9 Dezimalziffern für alle Fälle ausreichend? Wie printf("%.9g", x);?

Gibt es eine Standardfunktion, die einen Float in einen String mit der für diesen Wert erforderlichen Mindestanzahl von Dezimalstellen umwandelt, wenn 7 oder 8 ausreichen, damit keine unnötigen Ziffern gedruckt werden?

Hinweis: Ich kann keine hexadezimalen Fließkomma-Literale verwenden, da Standard C++ sie nicht unterstützt.

+1

Verwenden 1000 Stellen und befestigen Sie die hinteren Nullen keine solche Standard-Bibliothek ist! ;) –

+4

wie Sie nicht einen binären basierte Schwimmer Dezimalbruch ohne einen Fehler umwandeln können, würde ich vorschlagen, nur die binäre Darstellung Dumping (oder eine Mantisse + Exponenten getrennt). – Vlad

+5

@Vlad können Sie nicht? Sind nicht alle binären Brüche als endliche Dezimalzahl darstellbar? –

Antwort

16

Um zu gewährleisten, dass eine binary-> Dezimal-> binäre Umlauf gewinnt den ursprünglichen binären Wert, IEEE 754 erfordert


The original binary value will be preserved by converting to decimal and back again using:[10] 

    5 decimal digits for binary16 
    9 decimal digits for binary32 
    17 decimal digits for binary64 
    36 decimal digits for binary128 

For other binary formats the required number of decimal digits is 

    1 + ceiling(p*log10(2)) 

where p is the number of significant bits in the binary format, e.g. 24 bits for binary32. 

In C sind die Funktionen, die Sie für diese Umwandlungen sind snprintf verwenden können() und strtof/strtod/strtold().

Natürlich können in manchen Fällen sogar mehr Ziffern nützlich sein (nein, sie sind nicht immer "Rauschen", abhängig von der Implementierung der Dezimalkonvertierungsroutinen wie snprintf()). Betrachten wir z.B. printing dyadic fractions.

+0

+1 Für die standard-geprüfte Antwort (obwohl eine IEEE-konforme Implementierung vorausgesetzt wird, aber wer IEEE nicht verwendet, schwebt trotzdem). –

+0

@ChristianRau: Jeder nutzt heutzutage mehr oder weniger IEEE-Floats. Die Einschränkung besteht jedoch darin, ob die Dezimalwandlungsfunktionen für alle Eingaben korrekt gerundet sind, was vielleicht weniger sicher ist.Aber wenn Sie eine Dezimaldarstellung benötigen, können Sie nicht viel tun (außer das Implementieren eigener fehlerfreier Dezimalkonvertierungen, viel Glück!). – janneb

+0

@ChristianRau, janneb: Es ist nicht universell. Ich muss mich immer noch gelegentlich mit seismischen Daten befassen, die im IBM Gleitkommaformat erzeugt wurden! –

1

Die Fließkomma-zu-Dezimal-Konvertierung, die in Java verwendet wird, liefert garantiert die geringste Anzahl von Dezimalziffern hinter dem Dezimaltrennzeichen, das benötigt wird, um die Zahl von ihren Nachbarn zu unterscheiden (mehr oder weniger).

Sie können den Algorithmus von hier kopieren: http://www.docjar.com/html/api/sun/misc/FloatingDecimal.java.html Achten Sie auf die FloatingDecimal(float) Konstruktor und der toJavaFormatString() Methode.

+7

Genau wo in diesem 2800+ Zeilen Code startet der Algorithmus? – Gabe

+0

Technisch GPL belastet – sehe

+0

@Gabe Es beginnt im Konstruktor und endet in der Methode, die ich angeben. Die vollständige Zeichenfolge wird vom Ausdruck 'new FloatingDecimal (number) .toJavaFormatString()' zurückgegeben. – Joni

0

Sie können sprintf verwenden. Ich bin nicht sicher, ob diese beantwortet Ihre Frage genau zwar, aber irgendwie, hier ist der Beispielcode

#include <stdio.h> 
int main(void) 
{ 
float d_n = 123.45; 
char s_cp[13] = { '\0' }; 
char s_cnp[4] = { '\0' }; 
/* 
* with sprintf you need to make sure there's enough space 
* declared in the array 
*/ 
sprintf(s_cp, "%.2f", d_n); 
printf("%s\n", s_cp); 
/* 
* snprinft allows to control how much is read into array. 
* it might have portable issues if you are not using C99 
*/ 
snprintf(s_cnp, sizeof s_cnp - 1 , "%f", d_n); 
printf("%s\n", s_cnp); 
getchar(); 
return 0; 
} 
/* output : 
* 123.45 
* 123 
*/ 
+3

Dies ist ein vernünftiger Ansatz, aber eine Sache, die wir bei der Verwendung von sprintf gefunden haben, ist, dass die Rundung für verschiedene Plattformen unterschiedlich sein kann. –

+0

@Richard Ja U r right darüber ... – kapilddit

3

24 * (log (2)/log (10)) = 7,2247199

Das ist ziemlich repräsentativ für das Problem. Es macht überhaupt keinen Sinn, die Anzahl der signifikanten Ziffern mit einer Genauigkeit von 0,0000001 Ziffern auszudrücken. Sie konvertieren Zahlen in Text zugunsten eines Menschen, keine Maschine. Ein Mensch völlig wurscht, und würde viel lieber, wenn Sie

schrieb

24 * (log (2)/log (10)) = 7

Der Versuch, nur 8 signifikanten Stellen angezeigt werden erzeugt zufällige Rauschziffern. Bei Nicht-Null-Chancen ist 7 bereits zu viel, weil sich ein Gleitkommafehler in den Berechnungen anhäuft. Drucken Sie Zahlen vor allem mit einer sinnvollen Maßeinheit. Leute interessieren sich für Millimeter, Gramm, Pfund, Zoll und so weiter. Kein Architekt wird sich um die Größe eines Fensters kümmern, das genauer ausgedrückt als 1 mm ist. Keine Fensterfabrik wird versprechen ein so genaues Fenster.

Nicht zuletzt können Sie die Genauigkeit der Zahlen, die Sie in Ihr Programm eingeben, nicht ignorieren. Es ist nicht möglich, die Geschwindigkeit einer unbeladenen europäischen Schwalbe auf 7 Ziffern zu messen. Es ist ungefähr 11 Meter pro Sekunde, bestenfalls 2 Ziffern. Berechnungen mit dieser Geschwindigkeit durchzuführen und ein Ergebnis zu drucken, das mehr signifikante Ziffern hat, erzeugt unsinnige Ergebnisse, die Genauigkeit versprechen, die nicht da ist.

+10

* in einem anderen Programm zu verwenden * => es scheint, dass Ihre Annahme über einen Menschen beim Lesen der Ausgabe falsch ist. –

+0

Hmm, das hat sich ja nicht angemeldet. Seltsame Sache zu tun. Nun, einfache Lösung, solange ein Mensch es nie sieht, dann drucken Sie Ziffern in Hülle und Fülle. –

+1

Ich hasse es, wenn es so aussieht, als ob mein Gehirn das eine Importwort im Text bearbeitet hätte: x Es sieht auch so aus, als wäre Fred besorgt über den Platzverbrauch (und ich schätze Leistung). –

-1

Mit so etwas wie

def f(a): 
    b=0 
    while a != int(a): a*=2; b+=1 
    return a, b 

(dem Python ist) sollten Sie in einer verlustfreien Art und Weise Mantisse und Exponenten bekommen können.

In C, ist dies wahrscheinlich

struct float_decomp { 
    float mantissa; 
    int exponent; 
} 

struct float_decomp decomp(float x) 
{ 
    struct float_decomp ret = { .mantissa = x, .exponent = 0}; 
    while x != floor(x) { 
     ret.mantissa *= 2; 
     ret.exponent += 1; 
    } 
    return ret; 
} 

wäre aber bewusst sein, dass noch nicht alle Werte in dieser Art und Weise dargestellt werden kann, ist es nur ein kurzer Schuss, der die Idee geben sollte, aber wahrscheinlich verbessert werden muss.

+0

Es ist sehr hilfreich, -1 ohne Kommentar. – glglgl

2

Wenn das Programm von einem Computer gelesen werden soll, würde ich den einfachen Trick mit char* Aliasing tun.

  • alias float*-char*
  • Kopie in einen unsigned (oder was auch immer unsigned Typ ausreichend groß ist) über char* Aliasing
  • Druck der unsigned Wert

Decodierung nur den Prozess umkehrt (und auf den meisten Plattform kann eine direkte reinterpret_cast) verwendet werden.

+0

Sicher würde auch Alias ​​direkt auf 'unsigned *' funktionieren? – huon

+2

Und auf welche Weise gibt dies ein Float Literal? Natürlich können Sie immer die binäre Darstellung verwenden, um genaue Genauigkeit mit wenig Platz zu erhalten, aber gut ... –

+0

@ChristianRau: meine Idee war die anfängliche Anforderung, dass für zwei Programme zum Austausch von Gleitkommazahlen eine wörtliche Darstellung erforderlich wäre erforderlich. Da das Format normalisiert ist (und vom Standard vorgeschrieben ist), ist die Verwendung der binären Darstellung übertragbar (bei dem potentiellen Endianness-Problem). –

2

Wenn Sie eine C-Bibliothek haben, die C99 entspricht (und wenn Ihre Float-Typen eine Basis von 2 haben), kann das printf Format %a Fließkommawerte ohne Genauigkeit in hexadezimaler Form drucken, und Dienstprogramme wie scanf und strod in der Lage, sie zu lesen.

1

Wenn Sie diese Artikel lesen (siehe unten), werden Sie feststellen, dass es einen Algorithmus gibt, der die minimale Anzahl von Dezimalziffern so druckt, dass die Zahl unverändert interpretiert werden kann (d. H. Durch scanf).

Da es mehrere solche Zahlen geben kann, wählt der Algorithmus auch den nächsten Dezimalbruch zum ursprünglichen Binärbruch (ich nannte float value).

Schade, dass es in C.