Come si calcola in virgola mobile in un radix diverso da 10?
-
06-09-2019 - |
Domanda
Dato l'articolo di Wikipedia su Radix Point , come si potrebbe calcolare l'equivalente binario di 10.1 o l'equivalente esadecimale di 17,17? Per il primo, quello che è l'equivalente binario di un decimo? Per questi ultimi, la rappresentazione esadecimale di 17/100?
Sto cercando di più per un algoritmo che per le soluzioni di solo questi due esempi.
Soluzione
Per convertire decimale 10.1 a binario, separare le parti intere e frazionarie e convertire ogni separatamente.
Per convertire la parte intera, utilizzare divisione intera ripetuto da 2, e quindi scrivere i resti in ordine inverso:
10/2 = 5 restante 0
5/2 = 2 restante 1
2/2 = 1 resto 0
1/2 = 0 resto 1
Risposta: 1010
Per convertire la parte frazionaria, uso ripetuto moltiplicazione per 2, sottraendo fuori la parte intera ad ogni passo. I numeri interi, in ordine di generazione, rappresentano la vostra numero binario:
0.1 * 2 = 0.2
0.2 * 2 = 0.4
0.4 * 2 = 0,8
0.8 * 2 = 1.6
0.6 * 2 = 1.2
0.2 * 2 = 0.4
0.4 * 2 = 0,8
... (ciclo si ripete sempre)
Quindi decimale 0.1 è binario 0,000110011001100 ...
(Per una spiegazione più dettagliata si veda routine dec2bin_i () e dec2bin_f () nel mio articolo http://www.exploringbinary.com/base-conversion-in-php-using-bcmath/ .)
Per esadecimale, utilizzare la stessa procedura, se non con un divisore / moltiplicatore 16 invece di 2. I residui e numeri interi maggiori di 9 deve essere convertito in cifre esadecimali direttamente: 10 diventa A, 11 diventa B, ..., 15 diventa F.
Altri suggerimenti
Un numero di terminazione (un numero che può essere rappresentato da un numero finito di cifre) n 1 in base b 1 , può finire per essere un numero non fatale in una base diversa b 2 . Al contrario, un non-terminante numero in una base b 1 può rivelarsi un numero terminante in base b 2 .
Il numero 0.1 10 quando convertito in binario è un numero non fatale, come è 0,17 10 quando convertito in un numero esadecimale. Ma il numero di terminazione 0,1 3 in base 3, quando convertito in base 10 è la non-terminazione, ripetendo numero 0. (3) 10 (che indica che il numero 3 ripetizioni ). Analogamente, la conversione di 0,1 10 a binario e 0,17 10 in esadecimale, si finisce con il non-terminazione, ripetendo numeri 0.0 (0011) 2 e 0,2 (B851E) 16
A causa di questo, quando si converte un tale numero da una base all'altra, si possono trovare te dover approssimare il numero invece di avere una rappresentazione che è del tutto precisa.
L'algoritmo è abbastanza semplice, ma in pratica si può fare un sacco di modifiche, sia con tabelle di ricerca e registri per accelerarlo. Ma per l'algoritmo di base, si può provare qualcosa di simile:
shift=0;
while v>=base, v=v/base, shift=shift+1;
Next digit:
if v<1.0 && shift==0, output the decimal point
else
D=floor(v)
output D
v=v-D
v=v*base
shift = shift-1
if (v==0) exit;
goto Next Digit
Si può anche mettere un test in là per arrestare la stampa dopo N cifre per più ripetere decimali.
Il 'equivalente binario' di un decimo è una metà, cioè invece di 1/10 ^ 1, è 1/2 ^ 1.
Ogni cifra rappresenta una potenza di due. Le cifre dietro il punto di radice sono gli stessi, è solo che essi rappresentano 1 sopra la potenza di due:
8 4 2 1 . 1/2 1/4 1/8 1/16
Quindi, per 10.1, dovrete, ovviamente, un '8' e un '2' a fare la parte 10. 1/2 (0,5) è troppo, 1/4 (0.25) è troppo, 1/8 (0,125) è troppo. Abbiamo bisogno di 1/16 (0,0625), che ci lascerà con 0,0375. 1/32 è 0,03,125 mila, così possiamo fare anche questo. Finora abbiamo:
8 4 2 1 . 1/2 1/4 1/8 1/16 1/32
1 0 1 0 0 0 0 1 1
Con un errore di 0,00,625 mila. 1/64 (0,015,625 mila) e 1/128 (,0078,125 mila) sono entrambi troppo, 1/256 (,00,390625 millions) funzionerà:
8 4 2 1 . 1/2 1/4 1/8 1/16 1/32 1/64 1/128 1/256
1 0 1 0 0 0 0 1 1 0 0 1
Con un errore di ,00,234375 millions.
Il .1 non può essere espresso esattamente in binario (come 1/3 non può essere espresso esattamente in decimale). A seconda di dove si mette il radix, alla fine si deve fermare, probabilmente rotondo, ed accettare l'errore.
Prima di girarsi con questo alla luce della mia biblioteca GMP, qui è dove ho avuto modo di provare a rendere il codice PHP di Rick Regan generico per qualsiasi base da 2 fino a 36.
Function dec2base_f(ByVal ddecimal As Double, ByVal nBase As Long, ByVal dscale As Long) As String
Const BASES = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" 'up to base 36
Dim digitCount As Long
Dim wholeNumber As Double
Dim digit As String * 1
digitCount = 0
dscale = max(dscale, Len(CStr(ddecimal)) - Len("0."))
Dim baseary_f As String
baseary_f = "0."
Do While ddecimal > 0 And digitCount < dscale
ddecimal = ddecimal * nBase
digit = Mid$(BASES, Fix(ddecimal) + 1)
baseary_f = baseary_f & digit '"1"
ddecimal = ddecimal - Fix(ddecimal)
digitCount = digitCount + 1
Loop
dec2base_f = baseary_f
End Function
Function base2dec_f(ByVal baseary_f As String, nBase As Double) As Double
Const BASES As String = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
Dim decimal_f As Double
Dim i As Long
Dim c As Long
For i = Len(baseary_f) To Len("0.") + 1 Step -1
c = InStr(BASES, Mid$(baseary_f, i, 1)) - 1
decimal_f = decimal_f + c
decimal_f = decimal_f / nBase
Next
base2dec_f = decimal_f
End Function
Debug.Print base2dec_f(dec2base_f(0.09, 2, 200), 2) --> 0.09
Debug.Print base2dec_f(dec2base_f(0.09, 8, 200), 8) --> 0.09
Debug.Print base2dec_f(dec2base_f(0.09, 16, 200), 16) --> 0.09