为什么Clng在这些场景中的工作方式不同,并且可以在SQL Server中复制?(不是银行家的四舍五入)

为什么Clng在这些场景中的工作方式不同,并且可以在SQL Server中复制?(不是银行家的四舍五入),sql,vba,tsql,type-conversion,ms-access-2003,Sql,Vba,Tsql,Type Conversion,Ms Access 2003,执行以下语句将生成Access SQL: CLNG((CCUR(1.225)/1)*100) = 123 转换为:DecimalCurrencyDoubleDoubleLong 如果我删除CCUR转换功能: CLNG(((1.225)/1)*100) = 122 这里的转换是:DecimalDoubleDoubleLong 这两者有什么区别 这扩展到代码和Access SQL之间的差异 clng((CCUR(1.015)/1)*100)/100 = 1.01 (Wrong Rounding

执行以下语句将生成Access SQL:

CLNG((CCUR(1.225)/1)*100) = 123
转换为:
Decimal
Currency
Double
Double
Long

如果我删除CCUR转换功能:

CLNG(((1.225)/1)*100) = 122
这里的转换是:
Decimal
Double
Double
Long

这两者有什么区别

这扩展到代码和Access SQL之间的差异

 clng((CCUR(1.015)/1)*100)/100 = 1.01 (Wrong Rounding)
在Access SQL中

 clng((CCUR(1.015)/1)*100)/100 = 1.01 (Wrong Rounding)
在Access VBA中

 clng((CCUR(1.015)/1)*100)/100 = 1.02 (Appropriate Rounding Here)
Microsoft解释CLng函数使用

当小数部分正好为0.5时,CInt和CLng总是将其四舍五入到最近的偶数。例如,0.5轮到0,1.5轮到2。CInt和CLng不同于Fix和Int函数,它们截断而不是取整数字的小数部分。此外,Fix和Int始终返回与传入的值类型相同的值

看一个类似的问题和随后的答案,它解释了位计算在幕后会发生变化,这取决于它是如何计算的,但我不确定数据类型如何影响它

我遗漏了什么,为什么要这样计算?如何在
SQL Server
中可预测地再现此行为

编辑


经过一些挖掘,我相信这确实是一个舍入点问题的结果。在SQL server中,如果浮点数超出最大精度15位,则将浮点数四舍五入到最接近的整数。尽管
Double
相当于
TSQL
中的
Float(53)
,结果上的差异是两个不同问题的组合:Jet/ACE vs VBA表达式求值和十进制数的二进制浮点表示

第一个是Jet/ACE表达式引擎隐式地将小数转换为
Decimal
,而VBA将它们转换为
Double
。这很容易演示(注意
Eval()
函数使用Jet/ACE db引擎计算表达式):

第二个问题是关于安全的问题。这在某种程度上更难演示,因为VBA总是对其输出进行四舍五入,但使用另一种语言(本例中为Python)时问题更为明显:

VBA中的Double类型使用浮点运算,而Decimal类型使用整数运算(它在后台存储小数点的位置)

这样做的结果是,银行家四舍五入或传统四舍五入是一种误导决定因素是数字的二进制浮点表示形式是否略大于或小于其十进制表示形式。


若要在原始问题中了解这是如何工作的,请参见以下VBA:

?Eval("typename((CCUR(1.225)/1))"), Eval("typename(((1.225)/1))")
Double        Decimal
?Eval("typename(CCUR(1.225))"), Eval("typename(1.225)") 
Currency      Decimal
和Python:

>>> Decimal(1.225)
Decimal('1.225000000000000088817841970012523233890533447265625')

我还应该指出,在第二个示例中,您关于转换为Double的假设是不正确的。数据类型保持为
Decimal
,直到最后转换为
Long
前两个函数之间的区别在于,在Jet/ACE中将
十进制
货币
类型相乘会导致
双精度
这在我看来似乎有点奇怪,但代码证明了这一点:

?eval("TypeName(1.225)"), eval("TypeName(1.225)")
Decimal       Decimal

?eval("TypeName(CCUR(1.225))"), eval("TypeName((1.225))")
Currency      Decimal

?eval("TypeName(CCUR(1.225)/1)"), eval("TypeName((1.225)/1)")
Double        Decimal

?eval("TypeName((CCUR(1.225)/1)*100)"), eval("TypeName(((1.225)/1)*100)")
Double        Decimal

?eval("TypeName(CLNG((CCUR(1.225)/1)*100))"), eval("TypeName(CLNG(((1.225)/1)*100))")
Long          Long
所以这两种情况下的转换实际上是:

Decimal
Currency
Double
Double
Long
(如您正确假设的);及

Decimal
Decimal
Decimal
Decimal
Long
(更正您最初的假设)


要回答下面评论中的问题,
Eval()
使用与Jet/ACE相同的表达式引擎,因此在功能上相当于在Access查询中输入相同的公式。为了进一步证明,我提出以下内容:

SELECT
TypeName(1.225) as A1,
TypeName(CCUR(1.225)) as A2, 
TypeName(CCUR(1.225)/1) as A3,
TypeName((CCUR(1.225)/1)*100) as A4, 
TypeName(CLNG((CCUR(1.225)/1)*100)) as A5


在VBA中,浮点文本的数据类型是双精度的,而不是十进制的。那么,为什么这个select
select typename(1.015)
告诉我它是十进制的呢?因为这是访问SQL,而不是访问VBA。您需要清楚您在这里向我们展示的内容。@RBarryYoung谢谢您的反馈,我已经澄清了。可能相关:根据GSerg的评论和对本评论末尾问题的引用。Eval函数对数据类型有影响,当您直接执行MS Access SQL查询时,是否会重现这些数据类型@埃利亚斯:我只是好奇,当你为这个问题发布悬赏时,你还希望得到什么?我想我应该说得更清楚,但我更希望在SQL Server中重现舍入,而不是理解Access SQL和VBA之间的区别。我本应该说清楚的,但根据标题,答案只完成了一半。(虽然这是惊人的)
SELECT
TypeName(1.225) as A1,
TypeName(CCUR(1.225)) as A2, 
TypeName(CCUR(1.225)/1) as A3,
TypeName((CCUR(1.225)/1)*100) as A4, 
TypeName(CLNG((CCUR(1.225)/1)*100)) as A5
SELECT
TypeName(1.225) as B1,
TypeName((1.225)) as B2,
TypeName((1.225)/1) as B3,
TypeName(((1.225)/1)*100) as B4,
TypeName(CLNG(((1.225)/1)*100)) as B5