Formatta un numero in virgola mobile esattamente come decimale


9

Qualsiasi virgola mobile binaria può essere formattato esattamente in decimale. La stringa risultante potrebbe essere piuttosto lunga, ma è possibile. Nel mio articolo su virgola mobile tratterò l'importanza della precisione e ora voglio questa funzione. Questa sfida è scrivere un programma o una funzione che accetta un valore in virgola mobile come input e formatta una stringa decimale esatta come output.

Per garantire che stiamo lavorando con i numeri in virgola mobile corretti, è necessario fornire un formato preciso come input per il programma. Questo formato sarà di due numeri interi Significand Exponent, dove si trova il valore in virgola mobile effettivo Significand * 2 ^ Exponent. Si noti che entrambi i valori possono essere negativi.

specifiche:

  • L'intervallo e la precisione di almeno un float a 32 bit devono essere supportati (nessun input andrà oltre)
  • Il valore formattato decimale deve essere una rappresentazione esatta (semplicemente abbastanza vicino da garantire che un ritorno corretto al float non sia abbastanza buono)
  • Non ci fidiamo che le funzioni di formattazione in virgola mobile della libreria standard siano abbastanza corrette né abbastanza veloci (es:) printf, e quindi non possono essere utilizzate. È necessario eseguire la formattazione. Sono consentite funzioni di formattazione / conversione integrate.
  • Non possono esserci zeri iniziali o finali, ad eccezione di uno zero iniziale richiesto davanti allo zero .se non vi è alcun numero intero componente
  • È consentita una funzione o l'intero programma.

Esempi:

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

Il codice più corto vince.


3
È consentito l'uso dell'aritmetica in virgola mobile di precisione illimitata?
Dennis,

2
Se l'esponente non è negativo, possiamo concludere con .0?
Sp3000,

@Dennis: Sì, è consentita un'aritmetica di precisione fissa illimitata o elevata.
edA-qa mort-ora-y

1
Penso che sia incoerente. Se 0.abcnon è uno zero iniziale, allora non è uno zero abc.0finale.
orlp

1
È anche una convenzione terminare sempre con .0numeri interi quando si tratta di numeri in virgola mobile. Vedi ad esempio Python: str(1.0) == '1.0'contro str(1) == '1'. La tua logica è ancora incoerente.
orlp,

Risposte:


3

CJam, 43

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

Provalo online

Spiegazione:

Il programma funziona con esponenti fino a ± 999, vicino alla doppia precisione (64 bit). Separa il segno meno (se presente) dal significato, lo moltiplica per 10 999 quindi fa un po 'di movimento con l'esponente, che ora è un calcolo esatto. Quindi si sposta a sinistra con zeri se il risultato ha meno di 1000 cifre, separa le ultime 999 cifre come parte frazionaria, rimuove gli zeri finali convertendo il suo inverso in numero intero, se necessario aggiunge un punto decimale e rimette tutto insieme.

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

Alla fine, il segno meno (se presente) e la stringa finale vengono stampati automaticamente insieme.


2

CJam, 50 byte

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

Questo è un programma completo che legge da STDIN. Provalo online nell'interprete CJam .

Verifica tutti i casi di test contemporaneamente.


Sulla base del tuo commento, suppongo che CJam abbia una precisione illimitata e tu l'abbia usato qui? È corretto quindi che questa risposta copra qualsiasi input, non solo float a 32 bit? Inoltre, possiamo ottenere una spiegazione di come funziona?
edA-qa mort-ora-y

CJam ha una precisione illimitata per gli interi, ma solo float di doppia precisione. Mi moltiplico per una potenza di 20 per esponenti positivi e una potenza di 5 per quelli negativi, il cast per stringere e inserire il punto. Aggiungerò una spiegazione dettagliata tra poche ore.
Dennis,

E sì, data la memoria sufficiente, questo dovrebbe funzionare per qualsiasi input.
Dennis,

10 -2 sembra avere uno zero finale
aditsu ha smesso perché SE è MALE

@aditsu: Ah sì, uno zero finale per ogni potenza di 2 ...
Dennis

2

GNU sed + dc, 65

Il punteggio include +1 per l' -ropzione di sed .

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

Sono stato tentato di rivendicare questa dcrisposta unica C8k& 2r^*pper un punteggio di 10, ma dcho alcune stranezze di formattazione:

  • il segno -ve è _invece di-
  • le lunghe file sono spezzate da barre rovesciate
  • gli zeri finali devono essere rimossi
  • 0 iniziale per |n| < 1deve essere aggiunto

Quindi l'espressione cc è racchiusa e valutata sedper occuparsi di quanto sopra.

Uscita di prova:

$ 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
$ 

Hmm, sto pensando che questo dctipo di violazione della mia regola sull'uso di una funzione di formattazione standard.
edA-qa mort-ora-y

1
@ edA-qamort-ora-y Ho pensato che l'uso di dcè ok, dato che "l'aritmetica di precisione fissa illimitata o elevata è consentita" . dcIl pcomando non è una " funzione di formattazione in virgola mobile" - è una funzione di stampa di precisione arbitraria. Sto impostando la precisione su 128 cifre decimali ( C8k), che penso sia più che sufficiente per qualsiasi float a 32 bit.
Trauma digitale
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.