Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

IEEE-Gleitkommazahl dezimal ausgeben

15 views
Skip to first unread message

Lars Rohwedder

unread,
Jun 28, 2008, 12:39:34 PM6/28/08
to
Hallo,

aufgrund der Unfähigkeit von C, (IEEE-)Gleitkommazahlen "vernünftig"
als Dezimalzahl auszugeben, bin ich auf der Suche nach einem
Algorithmus, der das eben erledigt.

Meine Anforderungen an den Algorithmus sind dabei:

- Die Ausgabe muss "lesbar" sein. Das heißt: keine Exponential-
darstellung für Zahlen, deren Größenordnung es erlaubt, die Zahl
ohne 'n' führende oder hintere Nullen auszugeben)
- Die ausgegebene Zahl muss so kurz wie möglich sein. (Also so wenige
Dezimalziffern wie möglich)
- Die Ausgabe mus ein-eindeutig sein.
(Verschiedene Gleitkommazahlen ergeben verschiedene
Dezimaldarstellungen und umgekehrt. Solche Faxen wie NaNs und sowas
mal außen vor gelassen)

Klingt doch eigentlich nicht so schwer, dachte ich. Die Ausgaberoutinen
der Standardbibliothek von C bieten eine derartige Ausgabe leider
nicht; Entweder, es werden Stellen abgeschniten und die Ausgabe ist
nicht mehr (ein-)eindeutig, oder es werden zu viele Stellen ausgegeben,
die für eine Rückwandlung von dezimal in IEEE gar nicht nötig wären. :-(

Gleitkommazahlen sind ja stets Repräsentanten eines Intervalls aus den
reellen Zahlen. Jede dieser reellen Zahlen hat eine Dezimaldarstellung,
davon ist (mindestens) eine die kürzeste. Wird diese Dezimalzahl dann
wieder in eine Binärzahl umgewandelt und im IEEE-Format gespeichert,
kommt stets das ursprüngliche Bitmuster heraus.

Je nach den eingestellten Rundungsregeln (der Gleitkomma-Einheit im
Prozessor oder der Gleitkommabibliothek) liegt die Gleitkommazahl am
unteren oder oberen Rand des Intervalls oder in seiner Mitte. Dies muss
bei der Dezimalumwandlung natürlich berücksichtigt werden (die
libc-Ausgabefunktionen machen dies anscheinend nicht).


Wie müsste ein Algorithmus aussehen, der diese Anforderungen erfüllt
oder gibt es sowas vielleicht sogar schon fertig (in C oder C++ *g*)?

Ich bin für jeden konstruktiven Hinweis dankbar. :-)

Lars R.

Message has been deleted
Message has been deleted

Lars Rohwedder

unread,
Jun 28, 2008, 8:24:15 PM6/28/08
to
> Java erlaubt es auch, statt dessen auf einfache Weise genau den
> Dezimalbruch zu sehen, der dem internen Dualbruch entspricht

Sowas geht in C++ auch, über Zusatzbibliotheken. Ist aber nicht das,
was ich wollte, da nach meiner Auffassung Gleitkommazahlen eben ein
Intervall repräsentieren und keine einzelne Zahl. Diesen Dualbruch in
einen Dezimalbruch umwandeln ist zwar problemlos machbar, aber eher
selten der kürzestmögliche Dezimalbruch in dem repräsentierten
Intervall.

> [...] oder eine C-ähnliche Formatierung mit C-ähnlichen
> Formatspezifizierern zu verwenden.

Die C-Formatspezifizierer sind aber Müll, wie ich bereits angedeutet
hatte, denn bei 0.1 gibt er zu viele unnötige Nachkommastellen aus,
bei der Zufallszahl dagegen schneidet er ab.

Berücksichtigt die Java-Implementierung die verschiedenen
Rundungsmöglichkeiten, die bei der Errechnung der Gleitkommazahl
galten, bei der Dezimalumwandlung? Oder gibt es bei Java keine
verschiedenen Rundungsregeln?

Ab welchem Exponenten wird auf Exponentialdarstellung umgeschaltet? Ist
das ggf. konfigurierbar (mit sinnvollen Defaults?) ?

Danke trotzdem schonmal für deine Antworten. Es zeigt, das das Problem
zumindest in Ansätzen in anderen Programmiersprachen
zufriedenstellender gelöst wurde als in C. :-)

L.R.

Message has been deleted

Juergen Ilse

unread,
Jun 29, 2008, 12:00:31 AM6/29/08
to
Hallo,

Lars Rohwedder <l...@spamt.net> wrote:
> Die C-Formatspezifizierer sind aber Müll, wie ich bereits angedeutet
> hatte, denn bei 0.1 gibt er zu viele unnötige Nachkommastellen aus,
> bei der Zufallszahl dagegen schneidet er ab.

Es werden so viele Stellen angegeben, wie man im Formatstring spezifiziert
hat. Bei vielen Implementierungen sind das wohl bei "%f" 6 Nachkommastellen
wenn nichts spezifiziert wird. Man kann diesen Wert aber durch setzen des
"precision" Felds im Formatstring bestimmen.

> Ab welchem Exponenten wird auf Exponentialdarstellung umgeschaltet?

Bei "%f" gar nicht, bei "%e" immer und bei "%g" wird wahlweise die "%f"
oder die "%g" Formatierung verwendet (abhaengig von der Groesse des aus-
zugebenden Wertes).

> Ist das ggf. konfigurierbar (mit sinnvollen Defaults?) ?

Wenn du die Wahl des gewuenschten Format-Strings als "Konfiguration"
bezeichnen willst ...

Tschuess,
Juergen Ilse (jue...@usenet-verwaltung.de)
--
Ein Domainname (auch wenn er Teil einer Mailadresse ist) ist nur ein Name,
nicht mehr und nicht weniger ...

Lars Rohwedder

unread,
Jun 29, 2008, 9:25:26 AM6/29/08
to
Juergen Ilse <il...@usenet-verwaltung.de> wrote:
> Lars Rohwedder <l...@spamt.net> wrote:
>> Die C-Formatspezifizierer sind aber Müll, wie ich bereits angedeutet
>> hatte, denn bei 0.1 gibt er zu viele unnötige Nachkommastellen aus,
>> bei der Zufallszahl dagegen schneidet er ab.
>
> Es werden so viele Stellen angegeben, wie man im Formatstring spezifiziert
> hat. Bei vielen Implementierungen sind das wohl bei "%f" 6 Nachkommastellen
> wenn nichts spezifiziert wird. Man kann diesen Wert aber durch setzen des
> "precision" Felds im Formatstring bestimmen.

Ich weiß, ich habe die man-Pages (und den ISO-C99-Standard) gelesen.
Ich will aber keine "precision" angeben müssen, da sie entweder unnötig
viele oder aber zu wenige Stellen ausgibt und nur selten wirklich
"passt".

>> Ab welchem Exponenten wird auf Exponentialdarstellung umgeschaltet?

Die Frage bezog sich auf die Java toString()-Methoden, nicht auf das
Formatstring-Gefrickel von C.

> Bei "%f" gar nicht, bei "%e" immer und bei "%g" wird wahlweise die "%f"
> oder die "%g" Formatierung verwendet (abhaengig von der Groesse des aus-
> zugebenden Wertes).
>
>> Ist das ggf. konfigurierbar (mit sinnvollen Defaults?) ?
>
> Wenn du die Wahl des gewuenschten Format-Strings als "Konfiguration"
> bezeichnen willst ...

Würde ich, wenn ich das, was ich möchte, im Formatstring angeben könnte.
Aber das geht mit den ISO-C99-Formatstrings eben nicht.

L.

Lars Rohwedder

unread,
Jun 29, 2008, 9:52:50 AM6/29/08
to
Stefan Ram <r...@zedat.fu-berlin.de> wrote:

> Lars Rohwedder <l...@spamt.net> writes:
>>Berücksichtigt die Java-Implementierung die verschiedenen
>>Rundungsmöglichkeiten, die bei der Errechnung der Gleitkommazahl
>>galten, bei der Dezimalumwandlung? Oder gibt es bei Java keine
>>verschiedenen Rundungsregeln?
>
> Ich weiß jetzt nicht, was Du damit meinst. Vielleicht so etwas:
>
> »What actually happens is the computer only has 64 bits to
> [...]

Nein, das meinte ich nicht. Bei x86-FPUs, und inzwischen auch bei C99,
kann man angeben, ob eben auf- oder ab- oder anders gerundet werden
soll (fesetround() mit FE_DOWNWARD, FE_UPWARD, FE_TONEAREST,
FE_TOWARDZERO). Dies verändert eben das Intervall von reellen Zahlen,
das dann auf eine bestimmte IEEE-Gleitkommazahl abgebildet wird.

>>Ab welchem Exponenten wird auf Exponentialdarstellung
>>umgeschaltet? Ist das ggf. konfigurierbar (mit sinnvollen
>>Defaults?) ?
>

> Bei %g sind die Regeln ähnlich wie bei C, siehe unter der
> Überschrift »Float and Double« in
>
> http://download.java.net/jdk7/docs/api/java/util/Formatter.html

Meine Frage bezog sich nicht auf den C-Formatstringnachbau, sondern auf
die toString()-Methode darüber.

> sonst gibt es noch:
>
> http://download.java.net/jdk7/docs/api/java/text/DecimalFormat.html
>
> Interessant, zumindestens für die Fehlersuche, ist auch die in
> Standard-Java bereits vorhandene Möglichkeit, Gleitkommazahlen
> mit allen internen Bits hexadezimal darzustellen.

Das kann C99 auch. Da kommt das "%a" ja her. :-) Wirklich
"menschenlesbar" ist das aber IMHO nicht.

> Man kann auch die ULP erhalten und verwenden, um direkt zum
> nächsten Repräsentaten zu gelangen.
>
> http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Math.html#ulp(double)

Wenn ich das richtig lese, kriegt man sowas in C direkt mit der
Funktion nextafter() hin, oder?

> In einigen Fällen ist Java langsamer als C++, weil es richtig
> rechnen will.
>
> »What we do in the JVM on x86 is moderately obvious: we
> range check the argument, and if it's outside the range
> [-pi/4, pi/4] we do the precise range reduction by hand,
> and then call fsin.
>
> So Java is accurate, but slower.«

IMHO ist das für eine libc nicht verboten, dies auch zu tun, ehe sie -
sofern überhaupt vorhanden - die Sinusfunktion der FPU ruft. Wobei das
die FPU aber auch selber schon macht, da die Taylorreihe, mit der die
Sinusfunktion intern errechnet wird, sonst zu stark vom Sinus abweicht.

Mehr Genauigkeit bringt das alles aber nicht, wenn das ursprüngliche
Funktionsargument so groß ist, dass nicht mehr genügend darstellbare
Werte innerhalb einer Sinusperiode übrig bleiben und der Sinuswert
daher kaum noch Aussagekraft hat.

Aus diesem Grund begrenzen x86-FPUs das Argument bei sin() und cos()
auch auf ±2^63.

L.

Message has been deleted

Florian Weimer

unread,
Jun 30, 2008, 1:22:40 AM6/30/08
to
* Lars Rohwedder:

> aufgrund der Unfähigkeit von C, (IEEE-)Gleitkommazahlen "vernünftig"
> als Dezimalzahl auszugeben, bin ich auf der Suche nach einem
> Algorithmus, der das eben erledigt.

Das Standardwerk ist gdtoa.

> - Die Ausgabe mus ein-eindeutig sein.
> (Verschiedene Gleitkommazahlen ergeben verschiedene
> Dezimaldarstellungen und umgekehrt. Solche Faxen wie NaNs und sowas
> mal außen vor gelassen)

Das geht nicht.

Message has been deleted

Lars Rohwedder

unread,
Jun 30, 2008, 4:49:00 PM6/30/08
to
Florian Weimer <f...@deneb.enyo.de> wrote:
>> - Die Ausgabe mus ein-eindeutig sein.
>> (Verschiedene Gleitkommazahlen ergeben verschiedene
>> Dezimaldarstellungen und umgekehrt. Solche Faxen wie NaNs und sowas
>> mal außen vor gelassen)
>
> Das geht nicht.

Warum soll das nicht gehen? Nenne mir bitte mal eine
IEEE-Gleitkommazahl, die sich nicht in eine Dezimalzahl umwandeln
lässt, welche zückgeandelt wieder das ursprüngliche Bitmuster gibt.

L.

Lars Rohwedder

unread,
Jun 30, 2008, 4:55:10 PM6/30/08
to
> http://www2.hursley.ibm.com/decimal/decbibconversion.html

Danke für die URL. Dort finde ich im 4. Artikel(?):

Abstract: This paper presents a fast and accurate algorithm for
printing floating-point numbers in both free- and fixed-format modes.
In free-format mode, the algorithm generates the shortest, correctly
rounded output string that converts to the same number when read back
in, accommodating whatever rounding mode the reader uses. In
fixed-format mode, the algorithm generates a correctly rounded output
string using special # marks to denote insignificant trailing digits
[...]

Also scheint es ja doch möglich zu sein. Etwas anderes hätte mich auch
sehr überrascht. :-)

L.

Florian Weimer

unread,
Jul 1, 2008, 2:41:41 PM7/1/08
to
* Lars Rohwedder:

Die gibt's nicht (für eine geeignete Umwandlung), für "und umgekehrt"
bzw. "ein-eindeutig" reicht das aber noch nicht.

Florian Stock

unread,
Jul 2, 2008, 5:16:55 AM7/2/08
to

Lars Rohwedder <l...@spamt.net> writes:

> Florian Weimer <f...@deneb.enyo.de> wrote:
>>> - Die Ausgabe mus ein-eindeutig sein.
>>> (Verschiedene Gleitkommazahlen ergeben verschiedene
>>> Dezimaldarstellungen und umgekehrt. Solche Faxen wie NaNs und sowas
>>> mal außen vor gelassen)
>>
>> Das geht nicht.
>
> Warum soll das nicht gehen?

Das Problem sind z.B. Periodizitäten. Z.B. versuch mal 1/5 = 0.2 als
Binaere Zahl darzustellen. In dezimaler Schreibweise geht es ohne
Probleme, wenn du dich aber auf das Binäre beschränkst, geht es nicht
ohne unendlich viele Stellen. D.h. du musst irgendwann abbrechen. Daraus
folgt dann, dass die dezimale Zahl die durch diese abgebrochene binaere Zahl
darstellt wird und die dezimale Zahl die durch die endlosse binaere Zahl
dargestellt wird, dieselbe binaere Representation haben.

Florian

Lars Rohwedder

unread,
Jul 8, 2008, 8:59:12 AM7/8/08
to
Florian Stock <st...@esa.informatik.tu-darmstadt.de> wrote:
>
> Lars Rohwedder <l...@spamt.net> writes:
>
>> Florian Weimer <f...@deneb.enyo.de> wrote:
>>>> - Die Ausgabe mus ein-eindeutig sein.
>>>> (Verschiedene Gleitkommazahlen ergeben verschiedene
>>>> Dezimaldarstellungen und umgekehrt. Solche Faxen wie NaNs und sowas
>>>> mal außen vor gelassen)
>>>
>>> Das geht nicht.
>>
>> Warum soll das nicht gehen?
>
> Das Problem sind z.B. Periodizitäten. Z.B. versuch mal 1/5 = 0.2 als
> Binaere Zahl darzustellen. In dezimaler Schreibweise geht es ohne
> Probleme, wenn du dich aber auf das Binäre beschränkst, geht es nicht
> ohne unendlich viele Stellen. D.h. du musst irgendwann abbrechen.

Als Binärbruch wäre 0.2 in der Tat unendlich lang. Aber als
IEEE-Gleitkommazahl hat sie endliche Länge.

Wie ich schon schrob: Eine IEEE-Gleitkommazahl steht für ein Intervall
aus den reellen Zahlen, nämlich all jene Zahlen, die - je nach
Rundungsregeln - auf jenen endlichen Binärbruch abgebildet werden.

> Daraus folgt dann, dass die dezimale Zahl die durch diese
> abgebrochene binaere Zahl darstellt wird und die dezimale Zahl die
> durch die endlosse binaere Zahl dargestellt wird, dieselbe binaere
> Representation haben.

Ja, ich weiß. Es gibt beliebig viele reelle Zahlen und damit auch
beliebig viele Dezimalbrüche, die durch die gleiche IEEE-Gleitkommazahl
repräsentiert werden. Mindestens einer der Dezimalbrüche ist damit
gewiss der kürzeste. Und den will ich haben.

Also da 0.20000000000000000001 die gleiche
IEEE-Gleitkomma-Repräsentation hat wie 0.2, will ich bei der
Rückwandlung eben 0.2 haben.


Lars R.

Florian Stock

unread,
Jul 15, 2008, 4:32:42 AM7/15/08
to
Lars Rohwedder <l...@spamt.net> writes:

> Florian Stock <st...@esa.informatik.tu-darmstadt.de> wrote:
>>
>> Lars Rohwedder <l...@spamt.net> writes:
>>
>>> Florian Weimer <f...@deneb.enyo.de> wrote:
>>>>> - Die Ausgabe mus ein-eindeutig sein.

>>>> Das geht nicht.


>>>
>>> Warum soll das nicht gehen?
>>

> Also da 0.20000000000000000001 die gleiche


> IEEE-Gleitkomma-Repräsentation hat wie 0.2, will ich bei der
> Rückwandlung eben 0.2 haben.

Das kannst du machen, aber genau das heisst, dass es nicht ein-eindeutig
ist. Du kannst naemlich nicht beliebig hin-und-her-Konvertieren, weil
(in diesem Fall) 2 Dezimalzahlen dieselbe Gleitkommarepraesentanten
haben.

Flolo

Lars Rohwedder

unread,
Jul 17, 2008, 4:06:01 PM7/17/08
to
>> Also da 0.20000000000000000001 die gleiche
>> IEEE-Gleitkomma-Repräsentation hat wie 0.2, will ich bei der
>> Rückwandlung eben 0.2 haben.
>
> Das kannst du machen, aber genau das heisst, dass es nicht ein-eindeutig
> ist. Du kannst naemlich nicht beliebig hin-und-her-Konvertieren, weil
> (in diesem Fall) 2 Dezimalzahlen dieselbe Gleitkommarepraesentanten
> haben.

Okay, sorry, ich hatte mich da etwas missverständlich ausgedrückt.
Bijektiv geht es natürlich nicht, da es nur endlich viele verschiedene
IEEE-Zahlen gibt, aber unendlich viele verschiedene Dezimalbrüche. :-(

Dann spezifiziere ich es eben so:

Dezimalzahl
|
(verlustbehaftet)
|
V
IEEE-Zahl -->(verlustfrei)--> Dezimalzahl
^ |
`--------(verlustfrei)-------´

Jetzt klarer? :-)

Lars R.

0 new messages