Sformatuj liczbę zmiennoprzecinkową dokładnie w systemie dziesiętnym

9

Każdy binarny zmiennoprzecinkowy można sformatować dokładnie w systemie dziesiętnym. Wynikowy ciąg może być nieco długi, ale jest to możliwe. W moim artykule o zmiennoprzecinkowym omawiam znaczenie precyzji, a teraz chcę tę funkcję. Wyzwaniem jest napisanie programu lub funkcji, która pobiera wartość zmiennoprzecinkową jako dane wejściowe i formatuje dokładny ciąg dziesiętny jako dane wyjściowe.

Aby mieć pewność, że pracujemy z prawidłowymi liczbami zmiennoprzecinkowymi, należy podać dokładny format danych wejściowych do programu. Ten format będzie dwiema liczbami całkowitymi Significand Exponent, gdzie rzeczywista wartość zmiennoprzecinkowa to Significand * 2 ^ Exponent. Zauważ, że każda wartość może być ujemna.

Specyfika:

  • Zakres i precyzja co najmniej 32-bitowej liczby zmiennoprzecinkowej musi być obsługiwana (żadne dane wejściowe nie przekroczą tego)
  • Wartość dziesiętna po sformatowaniu musi być dokładnym odwzorowaniem (po prostu wystarczająco blisko, aby zagwarantować, że prawidłowy powrót zaokrąglenia do pływaka nie jest wystarczająco dobry)
  • Nie ufamy, że standardowe funkcje formatowania zmiennoprzecinkowego biblioteki są wystarczająco poprawne ani wystarczająco szybkie (np .:) printf, dlatego też nie mogą być używane. Musisz wykonać formatowanie. Zintegrowane funkcje formatowania / konwersji są dozwolone.
  • Nie może być żadnych zer wiodących ani końcowych, z wyjątkiem wymaganego jednego zera wiodącego przed, .jeśli nie ma składowej liczby całkowitej
  • Funkcja lub cały program jest dozwolona.

Przykłady:

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

Najkrótszy kod wygrywa.

edA-qa mort-ora-y
źródło
3
Czy dozwolone jest stosowanie arytmetyki zmiennoprzecinkowej o nieograniczonej precyzji?
Dennis
2
Jeśli wykładnik jest nieujemny, czy możemy na tym skończyć .0?
Sp3000,
@Dennis: Tak, dozwolona jest nieograniczona lub wysoka stała arytmetyka precyzji.
edA-qa mort-ora-y
1
Myślę, że to niespójne. Jeśli 0.abcnie jest wiodącym zerem, abc.0to nie jest końcowe.
orlp
1
Jest to również konwencja, aby zawsze kończyć się .0liczbami całkowitymi w przypadku liczb zmiennoprzecinkowych. Zobacz na przykład Python: str(1.0) == '1.0'versus str(1) == '1'. Twoja logika jest nadal niespójna.
orlp

Odpowiedzi:

3

CJam, 43

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

Wypróbuj online

Wyjaśnienie:

Program działa z wykładnikami do ± 999, blisko podwójnej precyzji (64 bity). Oddziela znak minus (jeśli jest obecny) od znaczenia, mnoży go przez 10 999, a następnie przesuwa nieco z wykładnikiem, który jest teraz dokładnym obliczeniem. Następnie wypełnia go zerami po lewej stronie, jeśli wynik ma mniej niż 1000 cyfr, oddziela 999 ostatnich cyfr jako część ułamkową, usuwa zera końcowe przez konwersję odwrotności na liczbę całkowitą, w razie potrzeby dodaje kropkę dziesiętną i składa wszystko z powrotem.

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

Na końcu znak minus (jeśli występuje) i końcowy ciąg są automatycznie drukowane razem.

aditsu przestało działać, ponieważ SE jest ZŁEM
źródło
2

CJam, 50 bajtów

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

Jest to pełny program, który czyta ze STDIN. Wypróbuj online w interpretatorze CJam .

Sprawdź wszystkie przypadki testowe jednocześnie.

Dennis
źródło
Na podstawie twojego komentarza zakładam, że CJam ma nieograniczoną precyzję i użyłeś go tutaj? Czy to prawda, że ​​ta odpowiedź obejmuje dowolne dane wejściowe, nie tylko 32-bitowe zmiennoprzecinkowe? Czy możemy również wyjaśnić, jak to działa?
edA-qa mort-ora-y
CJam ma nieograniczoną precyzję liczb całkowitych, ale tylko zmiennoprzecinkowe podwójnej precyzji. Mnożę przez potęgę 20 dla wykładników dodatnich i potęgę 5 dla ujemnych, odlewając do sznurka i wstawiając kropkę. Dodaję szczegółowe wyjaśnienie za kilka godzin.
Dennis
I tak, biorąc pod uwagę wystarczającą ilość pamięci, powinno to działać dla każdego wejścia.
Dennis
10 -2 wydaje się mieć końcowe zero
aditsu rzuciło, ponieważ SE to EVIL
@aditsu: Ach tak, jedno końcowe zero na każdą potęgę 2 ...
Dennis
2

GNU sed + dc, 65

Wynik obejmuje +1 dla -ropcji sed .

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

Kusiło mnie, by zażądać tej dcodpowiedzi C8k& 2r^*p- tylko 10 punktów, ale mam dcpewne dziwactwa formatowania:

  • _zamiast jest znak -ve-
  • długie linie są przerywane ukośnikami
  • końcowe zera muszą zostać usunięte
  • na początku |n| < 1należy dodać 0

Tak więc wyrażenie dc jest pakowane i sprawdzane, sedaby zająć się powyższym.

Wyjście testowe:

$ 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
$ 
Cyfrowa trauma
źródło
Hmm, myślę, że to dcnarusza moją zasadę używania standardowej funkcji formatowania.
edA-qa mort-ora-y
1
@ edA-qamort-ora-y Doszedłem do wniosku, że użycie dcjest w porządku, biorąc pod uwagę, że „dozwolona jest arytmetyka nieograniczona lub wysoka stała precyzja” . dc„s ppolecenie nie jest« pływający punkt funkcję formatowania» - to dowolna funkcja precyzyjnego druku. Ustawiam precyzję na 128 miejsc po przecinku ( C8k), co moim zdaniem jest więcej niż wystarczające dla dowolnej liczby 32-bitowej.
Cyfrowa trauma