鉴于维基百科关于Radix Point的文章,如何计算 10.1 的二进制等价物或 17.17 的十六进制等价物?对于前者,十分之一的二进制等价物是多少?对于后者,17/100 的十六进制表示?
我正在寻找一种算法,而不是仅仅针对这两个示例的解决方案。
鉴于维基百科关于Radix Point的文章,如何计算 10.1 的二进制等价物或 17.17 的十六进制等价物?对于前者,十分之一的二进制等价物是多少?对于后者,17/100 的十六进制表示?
我正在寻找一种算法,而不是仅仅针对这两个示例的解决方案。
要将十进制 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 .
该算法非常简单,但在实践中,您可以使用查找表和日志进行大量调整以加快速度。但是对于基本算法,您可以尝试这样的事情:
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 位之后停止打印,以获得更长的重复小数。
以 b 1为基数的终止数(可以由有限位数表示的数字)n 1可能最终成为不同基数 b 2的非终止数。相反,一个底数b 1中的非终止数可能会变成底数b 2中的终止数。
转换为二进制时的数字 0.1 10是非终止数,转换为十六进制数时的 0.17 10也是如此。但是以 3 为底的终止数字 0.1 3转换为以 10 为底的数字是非终止的重复数字 0.(3) 10(表示数字 3 重复)。类似地,将 0.1 10转换为二进制,将 0.17 10转换为十六进制,最终会得到非终止的重复数字 0.0(0011) 2和 0.2(B851E) 16
因此,在将此类数字从一个基数转换为另一个基数时,您可能会发现自己不得不近似该数字,而不是获得完全准确的表示。
十分之一的“二进制等价物”是二分之一,即不是 1/10^1,而是 1/2^1。
每个数字代表二的幂。小数点后面的数字是相同的,只是它们代表 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 不能用十进制精确表示一样)。根据您放置基数的位置,您最终必须停下来,可能会舍入并接受错误。
在我根据我的 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