Jok*_*oku 6 excel vba excel-365
我正在尝试比较表中的两个单元格:
使用公式计算“MR”列=ABS([@Value]-A1)以确定“值”列的移动极差。“值”列中的值未四舍五入。“MR”列(B3 和 B4)中突出显示的单元格是相等的。我可以将公式输入=B3=B4到单元格中,Excel 会显示 B3 等于 B4。
但是当我在VBA中比较它们时,VBA说B4大于B3。我可以选择单元格 B3 并在“立即窗口”中输入以下内容? selection.value = selection.offset(1).value。该语句的评估结果为 false。
我尝试从公式中删除绝对值,认为这可能与它有关,但 VBA 仍然说它们不相等。
我尝试添加另一行,其中 Value=1.78,因此 MR=0.18。有趣的是,新行(B5)中的 MR等于B3,但不等于B4。
然后我尝试增加 A4 的小数以匹配其他值,现在 VBA 说它们相等。但是当我将绝对值添加回公式时,VBA 再次表示它们不相等。我再次删除了绝对值,现在 VBA 说它们不相等。
为什么 VBA 告诉我单元格不相等,而 Excel 却说它们相等?今后如何通过 VBA 可靠地处理这种情况?
Exc*_*ero 12
问题在于IEEE 754 浮点运算标准在设计上并不精确。几乎所有编程语言都因此而受到影响。
\nIEEE 754 是一个极其复杂的主题,当您研究了几个月并认为自己完全理解时,您只是在欺骗自己!
\n准确的浮点值比较很困难并且容易出错。在尝试比较浮点数之前请仔细考虑!
\nExcel 程序通过在应用程序端作弊来解决这个问题。另一方面,VBA 忠实地遵循双精度( binary64 ) 的 IEEE 754 规范。
\nDouble 值在内存中使用 64 位表示。这 64 位分为三个不同的字段,用于二进制科学记数法:
\n该系统中的尾数利用了所有二进制数都以 和 开头的事实,1因此它1不存储在位模式中。这意味着将正常值的尾数精度提高到 53 位。
数学原理如下:存储值 = SIGN VALUE * 2^UNBIASED EXPONENT * MANTISSA
\n1注意,符号位的存储值表示负符号值(-1),而a0表示正符号值(+1)。公式为SIGN VALUE = (-1) ^ (sign bit)。
问题总是归结为同一件事。
\n\n\n绝大多数实数无法在该系统中精确表达,这会引入像杂草一样传播的小舍入误差。
\n
将此系统视为规则间隔点的网格可能会有所帮助。该系统只能表示点值,不能表示点之间的实数。分配给浮点数的所有值都将四舍五入到一个点值(通常是最近的点,但有些模式强制向上舍入到下一个最高点,或向下舍入)。对浮点值进行任何计算实际上都会保证结果值需要四舍五入。
\n为了强调显而易见的事实,该网格上相邻的可表示点值之间存在无限多个实数;并且所有这些都四舍五入到离散的网格点。
\n更糟糕的是,当网格远离真正的零(在两个方向上)扩展时,间隙大小在每次二次方时都会加倍。2例如,对于to范围内的值,网格点之间的间隙长度是to4范围内的值的两倍。当表示足够大的值时,网格间隙长度变得很大,但接近真正的零时,它很小。12
用你的例子数字...
\n1.24用以下二进制表示:
符号位 = 0
\n指数 = 01111111111
\n尾数 = 0011110101110000101000111101011100001010001111010111
\n完整 64 位的十六进制模式恰好是:3FF3D70A3D70A3D7。
\n精度完全源自 53 位尾数,二进制的精确十进制值为:\n0.23999999999999999911182158029987476766109466552734375
\n在这种情况下,1与尾数关联的隐藏位隐含了前导整数,因此完整的十进制值为:
1.2399999999999999911182158029987476766109466552734375
\n现在请注意,这并不完全1.24是问题的全部。
让我们检查一下1.42:
符号位 = 0
\n指数 = 01111111111
\n尾数 = 0110101110000101000111101011100001010001111010111000
\n完整 64 位的十六进制模式恰好是:3FF6B851EB851EB8。
\n暗示1完整的十进制值存储为:
1.4199999999999999289457264239899814128875732421875000
\n再说一遍,不完全是1.42。
现在,让我们检查一下1.6:
符号位 = 0
\n指数 = 01111111111
\n尾数 = 1001100110011001100110011001100110011001100110011010
\n完整 64 位的十六进制模式恰好是:3FF999999999999A。
\n\n\n请注意,在这种情况下,当尾数位用完时,重复的二进制分数会被截断\n并四舍五入?显然,1.6 当以二进制基数 2 表示时,永远无法精确地表示,就像 1/3 永远无法以十进制基数 10 精确表示一样(0.33333333333333333333333... \xe2\x89\xa0 1/3)。
\n
暗示1完整的十进制值存储为:
1.6000000000000000888178419700125232338905334472656250
\n不完全是1.6,但比其他人更接近!
现在让我们减去完整存储的双精度表示:
\n1.60- 1.42= 0.18000000000000015987
1.42- 1.24= 0.17999999999999993782
正如您所看到的,它们根本不平等。
\n解决这个问题的常用方法是阈值测试,基本上是检查两个值是否足够接近......这取决于您和您的要求。请预先警告,有效的阈值测试比乍一看要困难得多。
\n这是一个可以帮助您开始比较两个双精度数字的函数。它可以很好地处理许多情况,但不能处理所有情况,因为没有函数可以。
\nFunction Roughly(a#, b#, Optional within# = 0.00001) As Boolean\n Dim d#, x#, y#, z#\n \n Const TINY# = 1.17549435E-38 \'SINGLE_MIN\n \n If a = b Then Roughly = True: Exit Function\n \n x = Abs(a): y = Abs(b): d = Abs(a - b)\n \n If a <> 0# Then\n If b <> 0# Then\n z = x + y\n If z > TINY Then\n Roughly = d / z < within\n Exit Function\n End If\n End If\n End If\n \n Roughly = d < within * TINY\nEnd Function\nRun Code Online (Sandbox Code Playgroud)\n这里的想法是,True如果两个 Double 在一定范围内大致相同,则函数返回:
MsgBox Roughly(3.14159, 3.141591) \'<---dispays True\nRun Code Online (Sandbox Code Playgroud)\n内边距默认为 0.00001,但您可以传递所需的任何边距。
\n虽然我们知道:
\nMsgBox 1.60 - 1.42 = 1.42 - 1.24 \'<---dispays False\nRun Code Online (Sandbox Code Playgroud)\n考虑一下这个的实用性:
\nMsgBox Roughly(1.60 - 1.42, 1.42 - 1.24) \'<---dispays True\nRun Code Online (Sandbox Code Playgroud)\n@chris neilsen 链接到一个关于 Excel 和 IEEE 754 的有趣的Microsoft 页面。
\n请阅读 David Goldberg 的开创性著作《每个计算机科学家应该了解浮点运算》。它改变了我理解浮点数的方式。
\n| 归档时间: |
|
| 查看次数: |
577 次 |
| 最近记录: |