DAX中不同数据类型的舍入误差

原创:      2023-05-17 19:31


DAX中不同数据类型的舍入误差

2023-05-17 18:35·PowerPivot工坊


本文翻译自Marco Russo & Alberto Ferrari的文章—《Rounding errors with different data types in DAX》来源:SQLBI 本文描述DAX中可能出现的舍入差异。它们与数据类型和正在执行的操作相关:了解这些细节可以帮助您编写更健壮的DAX公式,并避免比较中的错误。


选择数据类型的第一个原因是支持数字范围和精度。但是,数学运算的结果可能产生无法用所选数据类型表示的数字,这需要进行舍入运算。因此,根据数据类型和执行顺序的不同,同一个操作序列的结果可能会产生不同的结果。在本文中,我们将讨论每种数据类型的典型舍入行为,以及如何避免DAX公式中可能出现的问题,因为结果与您的期望存在差异。


整数


两个整数值之间的和、差、乘总是返回一个整数值。如果结果超出支持的值范围(从-9,223,372,036,854,775,808或-2^63到9,223,372,036,854,775,807或2^63-1),则会出现溢出情况,但不会出现溢出错误,并且结果是可表示的最低有效数字。例如,考虑下面的表达式:

CONVERT ( 5E18, INTEGER ) + CONVERT ( 5E18, INTEGER )

整数结果应该是1E19,但是因为它是一个不能用64位整数表示的数字,所以表达式的结果是一个负数:-8,446,744,073,709,551,616

两个整数之间的除法不会产生溢出:它的结果是一个浮点值,因此对该数据类型的聚合受后面描述的规则的约束。


货币(又名固定十进制数)

只要在货币和整数或其他货币值之间使用求和和减法运算符,结果仍然是货币数据类型,在发生溢出时受整数行为的影响—因为货币的底层表示是整数数据类型。

例如,考虑下面的表达式:

CONVERT(5E14,CURRENCY)+CONVERT(5E14,CURRENCY)

结果应该是1E15,但是因为它是一个不能用货币数据类型表示的数字,所以表达式的结果是一个负数:-844,674,407,370,955.1616

乘积和除法运算符可以为结果引入数据类型转换。涉及一种货币数据类型的乘积的结果仍然是货币,除非将两种货币值相乘,在这种情况下,结果是小数。这有三个后果:

  • 货币和整数之间的乘法永远不会产生溢出错误,但在溢出的情况下可能会导致不准确的数字。

  • 如果结果不能表示为货币值,则货币与浮点数之间的乘法会产生溢出错误。

  • 一种货币与另一种货币值之间的乘法永远不会产生溢出错误,但作为浮点数返回的结果有15位有效数字。


最后一个例子很有趣,因为它展示了一个相同的操作会根据乘法中涉及的数据类型产生不同的结果。例如,根据第二个数字的数据类型,下面两个相同数字之间的乘法会产生不同的结果。通过将一种货币乘以一个浮点数,得到的结果是一种货币可以有超过15位有效数字。当同一运算将两种货币相乘时,结果四舍五入到15位更有效的数字-浮点结果的精度为。4000,而不是小数部分的。3616:

CONVERT ( 1234567890123.4567, CURRENCY ) * CONVERT ( 111, DOUBLE )

-- Result is 13,703,703,580,370.3616 (currency)

CONVERT ( 1234567890123.4567, CURRENCY ) * CONVERT ( 111, CURRENCY )

-- Result is 13,703,703,580,370.4000 (double)

当有两个十进制数并且结果需要超过4位有效小数时,两个货币值之间的乘法提供了优势。例如,下面的货币和浮点数之间的乘法产生一个四舍五入到最接近的4位小数的货币。两种货币数据类型之间的相同乘法返回带有5位十进制数字的浮点值:

CONVERT ( 1.2345, CURRENCY ) * CONVERT ( 0.1, DOUBLE )

-- Result is 0.1235 (currency)

CONVERT ( 1.2345, CURRENCY ) * CONVERT ( 0.1, CURRENCY )

-- Result is 0.12345 (double)

货币数据类型具有比浮点数更多的有效数字,但在乘法中必须小心使用。

当分母是货币时,除法的结果总是一个小数;当分子是货币而分母不是货币时,除法的结果是货币。当货币是除法的分子时,结果可能四舍五入到货币数据类型中可以表示的最接近的值:

CONVERT ( 1.2345, CURRENCY ) / CONVERT ( 2, INTEGER )

-- Result is 0.6173

CONVERT ( 1.2345, CURRENCY ) / CONVERT ( 2, DOUBLE )

-- Result is 0.6173

CONVERT ( 1.2345, CURRENCY ) / CONVERT ( 2, CURRENCY )

-- Result is 0.61725

CONVERT ( 1.2345, DOUBLE ) / CONVERT ( 2, CURRENCY )

-- Result is 0.61725


浮点数


由于浮点数有15位有效数字,因此根据求值的顺序,求和和减法运算符都可以提供不同的结果。相比之下,求和和相减的准确性分别可以管理最多18位和19位有效数字,整数和货币数据类型在其支持的值范围内。在选择货币数据类型而不是浮点值时,准确性可能是一个决定性因素:只要聚合值可以表示为货币数据类型,精度就更好。

例如,考虑三个数字A、B和C的和,其中A和C加起来等于零。由于浮点数保证的精度,求和顺序产生不同的结果。在第一个例子中,A和B的和产生了一个不能用15位数字完全表示的数字-所以这个数字是四舍五入的-当对C求和并减去之前由A添加的金额时,结果与B不同。

EVALUATE

VAR A = CONVERT ( 99999999999.9999, DOUBLE )

VAR B = CONVERT ( .0002, DOUBLE )

VAR C = CONVERT ( -99999999999.9999, DOUBLE )

RETURN ROW (

"A + B + C", FORMAT ( A + B + C, "#,0.0000000000000000" ),

"A + C + B", FORMAT ( A + C + B, "#,0.0000000000000000" )

)


通过使用货币数据类型重复相同的操作,无论操作顺序如何,结果都是正确的,因为每个中间结果都在货币可以表示的数字范围内:

EVALUATE

VAR A = CONVERT ( 99999999999.9999, CURRENCY )

VAR B = CONVERT ( .0002, CURRENCY )

VAR C = CONVERT ( -99999999999.9999, CURRENCY )

RETURN ROW (

"A + B + C", FORMAT ( A + B + C, "#,0.0000000000000000" ),

"A + C + B", FORMAT ( A + C + B, "#,0.0000000000000000" )

)


从实际的角度来看,货币数据类型在会计工作中被使用的很好,因为如果最大汇总值低于900万亿,它可以避免失去高达4个十进制数字的精度。使用浮点数会增加可以表示的值的范围,但会引入舍入差异。

此外,由于其内部表示,浮点数实际上可能不包含显示的数字,而是包含在浮点数本身精度范围内的数字。一旦将数字加在一起,这种细微的差异可能会变得明显,并且可以通过修改和的顺序来改变差异-如前面的示例所示。通过比较以不同方式计算的同一列的聚合,可以看到这种影响。

例如有一个Contoso数据库,Sales表中只有13,915行,分布在三年之间。每个月,我们只有几百笔交易。Sales[Net Price]列具有货币数据类型,而Sales[Net Price Decimal]列具有相同的以浮点形式存储的数字。我们使用A的单一汇总和B的逐日计算的总和来计算2018年1月的销售金额小数,差值在结果的第三列A - B中计算:

DEFINE

MEASURE Sales[Sales Amount Decimal] =

SUMX (

Sales,

Sales[Quantity] * Sales[Net Price Decimal]

)

EVALUATE

CALCULATETABLE (

VAR A = [Sales Amount Decimal]

VAR B =

SUMX (

'Date',

[Sales Amount Decimal]

)

RETURN

ROW (

"A", A,

"B", B,

"A - B", A - B,

"A = B", A = B

),

'Date'[Year Month] = "January 2018"

)

虽然A和B看起来是一样的,但它们的差别很小。这个差异意味着A和B之间的比较返回False。


如果使用Sales Amount标准度量重复相同的比较,该度量为Sales[Net Price]使用货币数据类型,则A和B之间的差值为0,并且A和B之间的比较返回True。

因为所有基于DAX函数的时间智能计算都会生成一个日期列表作为筛选器,所以使用时间智能函数时问题很常见。例如,我们将每个月的Sales Amount Decimal值与相应的当月累计(MTD)值进行比较——它们应该是相同的:

EVALUATE

SUMMARIZECOLUMNS (

'Date'[Year Month Number],

'Date'[Year Month],

"Sales Amount1", [Sales Amount Decimal],

"Sales Amount2", CALCULATE ( [Sales Amount Decimal], DATESMTD ( 'Date'[Date] ) ),

"Diff", [Sales Amount Decimal] - CALCULATE ( [Sales Amount Decimal], DATESMTD ( 'Date'[Date] ) )

)

ORDER BY 'Date'[Year Month Number]

这种差异不会影响所有月份,但很常见。


结论


数据类型的选择不仅对于定义可以存储在列中的值的范围,而且对于定义算术运算和聚合中支持的范围和精度都是至关重要的。在货币和浮点数(分别为Power BI中的Fixed Decimal Number和Decimal Number)之间进行选择需要仔细考虑所执行的操作和所需的精度。当舍入差异不是问题时,还应该考虑由数据类型转换引起的性能差异。

浮点值之间的比较应该始终包含一个容差范围。当使用浮点值数据类型时,最佳实践是不要使用=和==比较操作符。


转载请注明出处!


延伸阅读:

DAX数据类型及DAX表达式中的常见错误

一文带你了解DAX Studio

DAX实战 | 空白数据(Null值)知多少

从Power BI到Analysis Services:企业级数据分析实战

DAX实战 | FORMAT函数详解




如果您想深入学习微软Power BI,欢迎登录网易云课堂试听学习我们的“从Excel到Power BI数据分析可视化”系列课程。或者关注我们的公众号(PowerPivot工坊)后猛戳”在线学习”。




长按下方二维码关注“Power Pivot工坊”获取更多微软Power BI、PowerPivot相关文章、资讯,欢迎小伙伴儿们转发分享~


客户案例

按行业 按部门

线上课程

课程直通 讲师介绍

官方微信

联系我们

北京

13811982114
agileex@agileex.com

北京市朝阳区博雅国际中心A座601

上海

13811982114
agileex@agileex.com

上海市静安区沪太路453号302室

Copyright © 2022 北京敏捷艾科数据技术有限公司agileex.com - 京ICP备16008845号-1