Formatieren Sie eine Gleitkommazahl genau als Dezimalzahl

9

Jeder binäre Gleitkomma kann exakt dezimal formatiert werden. Die resultierende Zeichenfolge ist möglicherweise etwas lang, aber möglich. In meinem Artikel über Gleitkomma gehe ich auf die Bedeutung der Präzision ein und möchte nun diese Funktion. Diese Herausforderung besteht darin, ein Programm oder eine Funktion zu schreiben, die einen Gleitkommawert als Eingabe verwendet und eine exakte Dezimalzeichenfolge als Ausgabe formatiert.

Um sicherzustellen, dass wir mit den richtigen Gleitkommazahlen arbeiten, muss ein genaues Format als Eingabe für das Programm bereitgestellt werden. Dieses Format besteht aus zwei Ganzzahlen Significand Exponent, wobei der tatsächliche Gleitkommawert ist Significand * 2 ^ Exponent. Beachten Sie, dass jeder Wert negativ sein kann.

Besonderheiten:

  • Der Bereich und die Genauigkeit von mindestens einem 32-Bit-Float müssen unterstützt werden (keine Eingabe geht darüber hinaus).
  • Der dezimal formatierte Wert muss eine exakte Darstellung sein (einfach nah genug, um zu gewährleisten, dass eine korrekte Rundspitze zum Schweben nicht gut genug ist).
  • Wir vertrauen nicht darauf, dass Standard-Gleitkomma-Formatierungsfunktionen für Bibliotheken korrekt genug oder schnell genug sind (z. B. :) printf, und daher werden sie möglicherweise nicht verwendet. Sie müssen die Formatierung vornehmen. Integrierte Formatierungs- / Konvertierungsfunktionen sind zulässig.
  • Es dürfen keine führenden oder nachfolgenden Nullen vorhanden sein, außer der erforderlichen führenden Null vor der, .wenn keine Ganzzahlkomponente vorhanden ist
  • Eine Funktion oder ein ganzes Programm ist zulässig.

Beispiele:

1 -2 => 0.25
17 -3 => 2.125
-123 11 => -251904
17 50 => 19140298416324608
23 -13 => 0.0028076171875
3 120 => 3987683987354747618711421180841033728
3 -50 => 0.00000000000000266453525910037569701671600341796875
-3 -50 => -0.00000000000000266453525910037569701671600341796875
10 -2 => 2.5
-12345 -3 => -1543.125
0 0 => 0
161 -4 => 10.0625
512 -3 => 64

Der kürzeste Code gewinnt.

edA-qa mort-ora-y
quelle
3
Ist die Verwendung von Gleitkomma-Arithmetik mit unbegrenzter Genauigkeit zulässig?
Dennis
2
Wenn der Exponent nicht negativ ist, können wir damit enden .0?
Sp3000
@ Tennis: Ja, unbegrenzte oder hochpräzise Arithmetik ist erlaubt.
edA-qa mort-ora-y
1
Ich denke das ist inkonsistent. Wenn 0.abces sich nicht um eine führende Null handelt, abc.0handelt es sich nicht um eine nachfolgende Null .
Orlp
1
Es ist auch eine Konvention, .0bei Gleitkommazahlen immer mit ganzen Zahlen zu enden . Siehe zum Beispiel Python: str(1.0) == '1.0'versus str(1) == '1'. Ihre Logik ist immer noch inkonsistent.
Orlp

Antworten:

3

CJam, 43

r_'-&\ize999rim<s1e3'0e[W%999/(i_L?\+'.*sW%

Probieren Sie es online aus

Erläuterung:

Das Programm arbeitet mit Exponenten bis zu ± 999, nahezu doppelter Genauigkeit (64 Bit). Es trennt das Minuszeichen (falls vorhanden) vom Signifikanten, multipliziert es mit 10 999 und führt dann eine Bitverschiebung mit dem Exponenten durch, was nun eine exakte Berechnung ist. Es füllt dann links mit Nullen auf, wenn das Ergebnis weniger als 1000 Stellen hat, trennt die letzten 999 Stellen als Bruchteil, entfernt nachfolgende Nullen durch Konvertieren der Umkehrung in eine Ganzzahl, fügt bei Bedarf einen Dezimalpunkt hinzu und setzt alles wieder zusammen.

r_         read and duplicate the significand in string form
'-&        keep only the minus sign, if present
\          swap with the other copy of the significand
iz         convert to integer and get absolute value
e999       multiply by 10^999
ri         read the exponent and convert to integer
m<         shift left by it; negative values will shift right
            the result is an exact non-negative integer
s          convert to string
1e3'0e[    pad to the left with zero characters up to length 1000
            longer strings will be left intact
            we need 1 more than 999 for the 0.xxx case
W%         reverse the string
999/       split into slices of length 999
(          take out the first slice (reversed fractional part)
i          convert to integer
            this removes the leading zeros (trailing in reverse)
_L?        if it's zero, replace with an empty string
\+         concatenate back (to the left) with the second slice
'.*        join the with the dot character
            if the fractional part was zero, we only have the second slice
            (reversed integer part) and there is nothing to join
s          convert to string; this is the reversed result without the sign
W%         reverse back

Am Ende werden das Minuszeichen (falls vorhanden) und die endgültige Zeichenfolge automatisch zusammen gedruckt.

Aditsu beenden, weil SE böse ist
quelle
2

CJam, 50 Bytes

q~A1$z#\_0>K5?\z:E#@_s'-&oz*\md_sE'0e[W%isW%'.\+Q?

Dies ist ein vollständiges Programm, das aus STDIN liest. Probieren Sie es online im CJam-Interpreter aus .

Überprüfen Sie alle Testfälle gleichzeitig.

Dennis
quelle
Aufgrund Ihres Kommentars gehe ich davon aus, dass CJam unbegrenzte Präzision hat und Sie das hier verwendet haben? Ist es dann richtig, dass diese Antwort jede Eingabe abdeckt, nicht nur 32-Bit-Float? Können wir auch eine Erklärung bekommen, wie es funktioniert?
edA-qa mort-ora-y
CJam hat eine unbegrenzte Genauigkeit für ganze Zahlen, aber nur Floats mit doppelter Genauigkeit. Ich multipliziere mit einer Potenz von 20 für positive Exponenten und einer Potenz von 5 für negative Exponenten, um den Punkt zu fädeln und einzufügen. Ich werde in ein paar Stunden eine detaillierte Erklärung hinzufügen.
Dennis
Und ja, bei genügend Speicher sollte dies für jede Eingabe funktionieren.
Dennis
10 -2 scheint eine
nachfolgende
@aditsu: Ah ja, eine nachfolgende Null für jede Potenz von 2 ...
Dennis
2

GNU sed + dc, 65

Die Punktzahl beinhaltet +1 für die -rOption von sed .

y/-/_/
s/.*/dc -e"C8k& 2r^*p"/e
s/\\\n//
s/0+$//
s/^(-?)\./\10./

Ich war versucht, diese dceinzige Antwort C8k& 2r^*pfür eine Punktzahl von 10 zu beanspruchen , habe aber dceinige Formatierungsprobleme:

  • das -ve Zeichen ist _anstelle von-
  • lange Linien werden mit Backslashes unterbrochen
  • Nachgestellte Nullen müssen entfernt werden
  • führende 0 für |n| < 1muss hinzugefügt werden

Der DC-Ausdruck wird also umwickelt und ausgewertet sed, um das oben Gesagte zu erledigen .

Testausgabe:

$ echo "1 -2
17 -3
-123 11
17 50
23 -13
3 120
3 -50
-3 -50
8388608 127
1 -127" | sed -rf float.sed
0.25
2.125
-251904
19140298416324608
0.0028076171875
3987683987354747618711421180841033728
0.00000000000000266453525910037569701671600341796875
-0.00000000000000266453525910037569701671600341796875
1427247692705959881058285969449495136382746624
0.0000000000000000000000000000000000000058774717541114375398436826861112283890933277838604376075437585313920862972736358642578125
$ 
Digitales Trauma
quelle
Hmm, ich denke, das dcverstößt gegen meine Regel zur Verwendung einer Standardformatierungsfunktion.
edA-qa mort-ora-y
1
@ edA-qamort-ora-y Ich habe mir gedacht, dass die Verwendung von dcok ist, wenn "unbegrenzte oder hochpräzise Arithmetik erlaubt ist" . dcDer pBefehl ist keine " Gleitkomma-Formatierungsfunktion", sondern eine Druckfunktion mit beliebiger Genauigkeit. Ich setze die Genauigkeit auf 128 Dezimalstellen ( C8k), was meiner Meinung nach für jeden 32-Bit-Float mehr als ausreichend ist.
Digitales Trauma