Как вы вычисляете число с плавающей запятой в осях, отличных от 10? - PullRequest
3 голосов
/ 31 мая 2009

Учитывая статью Википедии о Radix Point , как рассчитать двоичный эквивалент 10,1 или шестнадцатеричный эквивалент 17,17? Для первого, что является двоичным эквивалентом десятой? Для последнего шестнадцатеричное представление 17/100?

Я больше ищу алгоритм, чем решения только для этих двух примеров.

Ответы [ 5 ]

6 голосов
/ 31 мая 2009

Чтобы преобразовать десятичную 10.1 в двоичную, разделите целые и дробные части и преобразуйте каждую в отдельности.

Чтобы преобразовать целочисленную часть, используйте повторное целочисленное деление на 2, а затем запишите остатки в обратном порядке:

10/2 = 5 остатков 0

5/2 = 2 остатка 1

2/2 = 1 остаток 0

1/2 = 0 остаток 1

Ответ: 1010

Чтобы преобразовать дробную часть, используйте повторное умножение на 2, вычитая целую часть на каждом шаге. Целые части в порядке генерации представляют ваше двоичное число:

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

... (цикл повторяется вечно)

Значит, десятичный 0,1 является двоичным 0,000110011001100 ...

(Более подробное объяснение см. В процедурах dec2bin_i () и dec2bin_f () в моей статье http://www.exploringbinary.com/base-conversion-in-php-using-bcmath/.)

Для шестнадцатеричного числа используйте ту же процедуру, но с делителем / множителем 16 вместо 2. Остатки и целые части больше 9 должны быть непосредственно преобразованы в шестнадцатеричные числа: 10 становится A, 11 становится B, ... 15 становится F.

3 голосов
/ 31 мая 2009

Завершающий номер (число, которое может быть представлено конечным числом цифр) n 1 в базе b 1 , может в конечном итоге стать не заканчивающимся номером в различная база b 2 . И наоборот, не заканчивающееся число в одной базе b 1 может оказаться конечным числом в базе b 2 .

Число 0,1 10 при преобразовании в двоичное число является не заканчивающимся числом, как и 0,17 10 при преобразовании в шестнадцатеричное число. Но конечное число 0,1 3 в базе 3 при преобразовании в базу 10 является бесконечным, повторяющимся числом 0. (3) 10 (означающее, что число 3 повторяется). Точно так же, преобразуя 0,1 10 в двоичную и 0,17 10 в шестнадцатеричную, в результате получаются бесконечные повторяющиеся числа 0,0 (0011) 2 и 0,2 B851E) 16

Из-за этого при преобразовании такого числа из одной базы в другую вы можете столкнуться с необходимостью приблизиться к числу, а не иметь представление, которое является абсолютно точным.

2 голосов
/ 31 мая 2009

Алгоритм довольно прост, но на практике вы можете выполнить множество настроек как с помощью таблиц поиска, так и журналов, чтобы ускорить его. Но для базового алгоритма вы можете попробовать что-то вроде этого:

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

Вы также можете поставить там тест, чтобы остановить печать после N цифр для более длинных повторяющихся десятичных знаков.

1 голос
/ 31 мая 2009

«Двоичный эквивалент» одной десятой составляет половину, то есть вместо 1/10 ^ 1, это 1/2 ^ 1.

Каждая цифра представляет собой степень двойки. Цифры за точкой отсчета одинаковы, просто они представляют 1 над степенью двойки:

 8 4 2 1 . 1/2 1/4 1/8 1/16 

Так что для 10.1 вам, очевидно, нужны '8' и '2', чтобы сделать 10-ю часть. 1/2 (0,5) - это слишком много, 1/4 (0,25) - это слишком много, 1/8 (0,125) - это слишком много. Нам нужно 1/16 (0,0625), что оставит нас с 0,0375. 1/32 - это 0,03125, поэтому мы тоже можем это принять. Пока что имеем:

 8 4 2 1 . 1/2 1/4 1/8 1/16 1/32
 1 0 1 0    0   0   0   1     1

с ошибкой 0,00625. 1/64 (0,015625) и 1/128 (0,0078125) слишком много, 1/256 (0,00390625) будет работать:

 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

С ошибкой 0,00234375.

.1 нельзя выразить точно в двоичном виде (точно так же, как 1/3 нельзя выразить точно в десятичном виде). В зависимости от того, куда вы положили основание, вам, в конечном счете, придется остановиться, возможно, округлить и принять ошибку.

0 голосов
/ 01 июня 2009

Прежде, чем я начну разбираться с этим в свете моей библиотеки GMP, вот где я попытался сделать PHP-код Рика Регана универсальным для любой базы от 2 до 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
...