Ms access Access.Application.Eval()的奇怪行为

Ms access Access.Application.Eval()的奇怪行为,ms-access,vba,Ms Access,Vba,解释为什么在这种情况下,Access.Application.Eval()(通常缩写为Eval())会产生与仅计算原始表达式不同的结果: Debug.Print Round(.575 * 100) 57 Debug.Print Eval("Round(.575 * 100)") 58 编辑:为了解决GSerg的答案,以下仍然返回不同的结果: Debug.Print Eval("Round(CSng(.575) * 100)") 57 Debug.Print Round(CSng(

解释为什么在这种情况下,
Access.Application.Eval()
(通常缩写为
Eval()
)会产生与仅计算原始表达式不同的结果:

Debug.Print Round(.575 * 100)
 57 
Debug.Print Eval("Round(.575 * 100)")
 58 

编辑:为了解决GSerg的答案,以下仍然返回不同的结果:

Debug.Print Eval("Round(CSng(.575) * 100)")
 57 
Debug.Print Round(CSng(.575) * 100)
 58 

我不使用Access,但我想Eval会将表达式作为Access表达式进行计算,因此Round和Int函数的操作可能与VBA版本不同


我从痛苦的经历中知道,VBA的Round()函数使用舍入到偶数的舍入方法(又名银行家舍入),而不是更常见的从零到五的舍入(又名对称算术舍入)方法。

这是因为不同的浮点精度

在一种情况下,常数被识别为
Double
s,在另一种情况下被识别为
Single
s

? math.round((.575! - Int(.575!)) * 100)
 58 
? math.round((.575# - Int(.575#)) * 100)
 57 

如果在Access中运行以下SQL表达式,则会得到58:

select Round((.575 - Int(.575)) * 100) as MyValue
如果在Access(以及任何Office VBA IDE)即时窗口中运行以下语句,则会得到57:

Round((.575 - Int(.575)) * 100)
所以,这让我相信VBA有一种不同的方式来进行循环而不是访问,可能更适用于JET


现在,这有什么不同?不知道……会找一个比我技术更好的人。

乘法在Eval()下返回一个不同的乘积,但我不明白为什么

Debug.Print (.575 * 100) < 57.5
True

Debug.Print Eval("(.575 * 100) = 57.5")
-1

格斯伯格让我思考。我开始相信,当调用Eval时,Jet试图将十进制文本强制为货币类型,而VBA则将十进制文本强制为双精度类型。例如:

? Math.Round(.575 * 100)
 57 
? Math.Round(CSng(.575) * 100)
 58 
? Math.Round(CDbl(.575) * 100)
 57 
? Math.Round(CCur(.575) * 100)
 58 

? Eval("Round(.575 * 100)")
 58 
? Eval("Round(CSng(.575) * 100)")
 57 
? Eval("Round(CDbl(.575) * 100)")
 57 
? Eval("Round(CCur(.575) * 100)")
 58 

出于同样的原因,
Debug.Print Round(0.575*100.0)、Round(57.5*1.0)
returns
57 58
?好的,那么为什么会发生这种情况:
Print Round(0.575*100.0)、Round(57.5*1.0)
returns
57 58
。都是双打。我想我需要阅读更多关于浮点运算的内容…@Jean-Yeah,浮点
0.575*100.0
可能并不完全等于
57.5*1.0
。至于这个问题,下面的说法也是正确的:
math.Round(.575*100#))=>57
math.Round(.575!*100!)=>58
。这是最合理的解释。我很满意+1@Jean:这是因为.5可以用二进制分数精确表示(即,二进制中的.1或1/2),而.575不能。请参阅Python教程中的简明而直接的解释。这可能是最有帮助的答案(为每个人指明了正确的方向),但是@HansUp对TypeName的使用提供了一个更好的证据来证明实际发生了什么。这不是问题:在OP的两种情况下,都调用VBA的
Round
。你发现了什么:VBA的
舍入
使用银行家舍入法,即将0.5舍入到最接近的偶数整数;而访问不需要。但同样,这并不是这个问题的重点。正如@GSerg猜测的,我猜这与将文字数字强制转换为不同的数据类型有关。我认为VBA强制加倍,Jet强制货币。看看我贴的答案。此外,二进制/浮点表示的.575计算结果为0.5749999999996,这可能就是
(.575*100)<57.5
好的原因。但Jet和这有什么关系?你是说Eval()通过Jet运行它的表达式吗?我在帮助主题中没有看到任何相关内容。没错,Eval()显然使用了Access Expression服务,它与Jet Expression服务()不同(尽管可能与之密切相关)。整个情况对我来说有点像一个黑匣子,我想知道更多关于里面发生了什么。我刚刚重新阅读了Eval()的帮助,这引起了我的注意:您可以使用Eval函数访问在Visual Basic中通常不可用的表达式运算符。例如,您不能在代码中直接使用…和之间或中的SQL运算符,但可以在传递给Eval函数的表达式中使用它们。由于和之间以及中的是Jet知道如何计算的表达式,我认为Jet以某种方式直接或间接地参与其中。早些时候我写了@GSerg的答案,当时是最合理的。。。但这一点是非常可信的,它是确凿的证据+2冰实验,但你忘了强迫100<代码>?Eval(“Round(CSng(.575)*CSng(100))”返回
58
。不管怎样,+1表示在发布答案之前做了一些相当广泛的测试——这在这方面还不够普遍。
? Math.Round(.575 * 100)
 57 
? Math.Round(CSng(.575) * 100)
 58 
? Math.Round(CDbl(.575) * 100)
 57 
? Math.Round(CCur(.575) * 100)
 58 

? Eval("Round(.575 * 100)")
 58 
? Eval("Round(CSng(.575) * 100)")
 57 
? Eval("Round(CDbl(.575) * 100)")
 57 
? Eval("Round(CCur(.575) * 100)")
 58