从Excel 2007中,我在阅读本网站时想到了这个VBA代码:将十进制转换为二进制分数
主要目标是提出一个将小数转换为二进制表示的函数。
下面是我的代码:
Sub DTBfraction() ' Decimal to Binary
Dim myFraction As Double
Dim tmpDEC As Double, tmpPRO As Double, tmpREM As Double
Dim i As Long
Dim tmpSTRING As String, tmpBIT As String
myFraction = 0.8
tmpDEC = myFraction
Do
i = i + 1
tmpDEC = tmpDEC * 2
tmpPRO = Int(tmpDEC)
tmpREM = tmpDEC - tmpPRO
tmpBIT = IIf(tmpPRO >= 1, "1", "0")
tmpSTRING = tmpSTRING & tmpBIT
If i > 2000 Then Stop ' needed to prevent cases of infinite looping
Debug.Print i, myFraction, tmpDEC, tmpPRO, tmpREM
tmpDEC = tmpREM
Loop Until tmpREM <= 0
Debug.Print "0b0." & tmpSTRING
End Sub
Rem Debug.Print i myFraction tmpDEC tmpPRO tmpREM
Rem Debug.Print 1 0.8 1.6 1 0.6
Rem Debug.Print 2 0.8 1.2 1 0.2
Rem Debug.Print 3 0.8 0.4 0 0.4
Rem Debug.Print 4 0.8 0.800000000000001 0 0.800000000000001
Rem Debug.Print 5 0.8 1.6 1 0.600000000000001
Rem Debug.Print 6 0.8 1.2 1 0.200000000000003
Rem Debug.Print 7 0.8 0.400000000000006 0 0.400000000000006
' ... up to 52 in that particular case
Rem Debug.Print 0b0.1100110011001100110011001100110011001100110011001101
遇到的问题是,在第4次循环时,代码将计算0.800000000000001,而不是得到0.8,这创建了一个不希望的循环多次的行为,直到条件将终止循环,直到tmpREM达到一个非常小的值,它被认为是0。实际上,这个循环应该是无限的,因为tmpREM永远不会变为零。这是一个周期性的情况。
经过一些实验和测试,发现0.6 0.2 0.4的前3个tmpREM值不是。相反,它们稍微高一点,但在Debug上不显示。打印行,但如果执行测试(如tmpREM>0.6,实际上tmpREM = 0.6会显示一个False状态。
的目标是通过编程捕获这种情况,以便代码停止无限地输出1和0,但我的解决方案是一个业余的,没有功能。我试着用10^12乘以结果,做一个INT()然后再乘以10^12。这不是一个可行的解决方案。减法也不起作用,而不是0.8 VBA吐出0.799999999999。
我觉得解决方案在于防止计算错误输出0.80000000000001而不是0.8
一旦实现,我可能能够想出一个解决方案来检测周期性计算并阻止代码循环。在这一点上,我失去了想象力。因此,问题是:
我如何才能摆脱这个计算错误,使我得到0.8而不是0.800000000000001 ?
二进制中的某些数字有无限的"小数",非常类似于十进制中的1/3和1/7。这些数字不能精确地用二进制表示;其中之一是1/10
小数可以四舍五入到n位:
n = 1
numff = int (num * 10 ^ n + 0.5)/10 ^ n
…但是当你把n取到更大的数时,它也有限制;又是由于计算机的数值精度。