如何计算10以外基数的浮点数?

时间:2009-05-31 15:34:33

标签: algorithm floating-point decimal-point radix radix-point

鉴于维基百科关于Radix Point的文章,如何计算二进制当量为10.1或十六进制当量为17.17?对于前者,十分之一的二进制当量是多少?对于后者,十六进制表示为17/100?

我正在寻找一种算法,而不仅仅是那两个例子的解决方案。

5 个答案:

答案 0 :(得分:6)

要将十进制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 ......

(有关更详细的说明,请参阅我的文章http://www.exploringbinary.com/base-conversion-in-php-using-bcmath/中的例程dec2bin_i()和dec2bin_f()。)

对于十六进制,使用相同的过程,除非除数/乘数为16而不是2.余数和大于9的整数部分必须直接转换为十六进制数字:10变为A,11变为B,..., 15变为F.

答案 1 :(得分:3)

基数b 1 中的终止号码(可由有限位数表示的数字)n 1 ,可能最终成为非终止号码在不同的基础b 2 。相反,一个基数b 1 中的非终止数可能会变成基数b 2 中的终止数。

转换为二进制时的数字0.1 10 是非终止数,当转换为十六进制数时,为0.17 10 。但是,当转换为基数10时,基数3中的终止数0.1 3 是非终止的,重复数为0.(3) 10 (表示数字3重复)。类似地,将0.1 10 转换为二进制,将0.17 10 转换为十六进制,最终得到非终止的重复数字0.0(0011) 2 和0.2(B851E) 16

因此,当将这样的数字从一个基数转换为另一个基数时,您可能会发现自己必须近似数字而不是具有完全准确的表示。

答案 2 :(得分:2)

该算法非常简单,但在实践中,您可以使用查找表和日志进行大量调整以加快速度。 但对于基本算法,您可以尝试这样的事情:

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位数后停止打印更长的重复小数。

答案 3 :(得分:1)

十分之一的'二进制等值'是一半,即不是1/10 ^ 1,它是1/2 ^ 1.

每个数字代表2的幂。基数点后面的数字是相同的,只是它们代表1超过2的幂:

 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不能用十进制精确表示)。根据放置基数的位置,最终必须停止,可能是圆形,并接受错误。

答案 4 :(得分:0)

在我根据我的GMP库进行讨论之前,我在这里尝试使Rick Regan的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