Como calcular ponto flutuante em uma raiz diferente de 10?
-
06-09-2019 - |
Pergunta
Dada artigo da Wikipedia sobre Radix Ponto , como seria um calcular o equivalente binário de 10,1 ou o equivalente hexadecimal de 17,17? Para o primeiro, o que é o equivalente binário de um décimo? Para este último, a representação hexadecimal de 17/100?
Eu estou olhando mais para um algoritmo de soluções para apenas esses dois exemplos.
Solução
Para converter decimal 10,1 para binário, separe os inteira e fracionária e converter cada um separadamente.
Para converter a parte inteira, o uso repetido inteiro divisão por 2, e em seguida, escrever os remanescentes na ordem inversa:
2/10 = 5 restante 0
5/2 = 2 restante 1
2/2 = 1 restante 0
02/01 = 0 restante 1
Resposta: 1010
Para converter a parte fraccionada, o uso repetido de multiplicação por dois, subtraindo a parte inteira em cada passo. As peças inteiras, na ordem da geração, representam o número binário:
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 se repete sempre)
Assim decimal 0.1 é binário ,000110011001100 ...
(Para uma explicação mais detalhada ver rotinas dec2bin_i () e dec2bin_f () em meu artigo http://www.exploringbinary.com/base-conversion-in-php-using-bcmath/ .)
Para hexadecimal, utilizar o mesmo procedimento, excepto com um divisor / multiplicador de 16 em vez de 2. O vasilhame e peças número inteiro maior do que 9 deve ser convertido para dígitos hexadecimais directamente: 10 torna-se um, 11 torna-se B, ..., 15 torna-se F.
Outras dicas
Um número de terminação (um número que pode ser representado por um número finito de dígitos) n 1 na base b 1 , pode acabar por ser um número não encerra em uma base diferente b 2 . Por outro lado, um número de não-encerramento em uma base de b 1 pode vir a ser um número de terminação na base b 2 .
O número 0,1 10 quando convertida em binário é um número de não-encerramento, como é 0,17 10 quando convertidos para um número hexadecimal. Mas, o número de terminação 0,1 3 na base 3, quando convertido para a base 10 é o não-terminal, repetindo-se o número 0. (3) 10 (significando que o número 3 repetições ). Do mesmo modo, a conversão de 0,1 10 para binário e 0,17 10 em hexadecimal, acaba-se com o não-terminal, os números de repetição de 0,0 (0011) 2 e 0,2 (B851E) 16
Devido a isso, ao converter um número tal de uma base para outra, você pode encontrar-se ter que aproximar o número em vez de ter uma representação que é completamente preciso.
O algoritmo é bastante simples, mas na prática você pode fazer um monte de ajustes tanto com tabelas de pesquisa e logs para acelerá-lo. Mas para o algoritmo básico, você pode tentar algo como isto:
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
Você também pode colocar um teste lá para parar a impressão depois de N dígitos para números decimais mais repetidos.
O 'equivalente binário' de um décimo é metade, ou seja, em vez de 1/10 ^ 1, é 1/2 ^ 1.
Cada dígito representa uma potência de dois. Os dígitos atrás do ponto de raiz são as mesmas, é só que eles representam 1 sobre o poder de dois:
8 4 2 1 . 1/2 1/4 1/8 1/16
Assim, para 10,1, você obviamente precisa de um '8' e '2' para fazer a parte 10. 1/2 (0,5) é demasiado, 1/4 (0,25) é demasiada, 1/8 (0,125) é demasiada. Precisamos de 1/16 (0,0625), o que vai nos deixar com 0,0375. 1/32 é 0,03125, para que possamos levar isso também. Até agora, temos:
8 4 2 1 . 1/2 1/4 1/8 1/16 1/32
1 0 1 0 0 0 0 1 1
Com um erro de 0,00625. 1/64 (0,015625) e 1/128 (0,0078125) são ambos muito, 1/256 (0,00390625) funcionará:
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
Com um erro de 0,00234375.
O .1 não podem ser expressos exatamente em binário (como 1/3 não podem ser expressos exatamente em decimal). Dependendo de onde você colocar a sua raiz, você acabará por ter de parar, provavelmente rodada, e aceitar o erro.
Antes de eu mexer com isso, à luz da minha biblioteca GMP, aqui é onde eu tenho que tentar tornar o código PHP do Rick Regan genérico para qualquer base de 2 até 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