Spark Double类型加减运算异常求助(AWS EMR Spark 2.4.4)
关于AWS EMR Spark 2.4.4中Double类型加减运算精度问题的解答
这个现象其实是IEEE 754浮点数的固有精度限制导致的,结合Spark的编译期优化,就出现了你看到的差异,下面详细解释并给出解决方案:
问题原因拆解
x列结果精确的原因:
当你直接写48.85 + 6.95 + -55.80这种数值字面量运算时,Spark会在编译阶段执行常量折叠(Constant Folding)——也就是提前计算好这个表达式的结果。这个过程中Spark可能使用了更高精度的算术处理(比如基于十进制的精确计算),所以最终得到了精确的0.00。y、z列出现精度误差的原因:
- y列:Spark SQL中对字符串使用
+运算符时,会隐式将字符串转换为Double类型再执行运算; - z列:显式
cast为Double后运算。
这两种情况都是在运行时执行IEEE 754标准的Double浮点运算,而像48.85、6.95这类十进制小数无法用二进制浮点数精确表示,运算时会产生微小的精度损失,累积后就出现了7.105427357601002e-15这类近似值。
- y列:Spark SQL中对字符串使用
解决方案
方案1:使用Decimal类型(推荐)
Decimal是十进制浮点数类型,可以精确表示十进制小数,完全避免这类精度问题。修改你的SQL如下:
spark.sql(""" select 48.85 + 6.95 + -55.80 x, cast('48.85' as decimal(10,2)) + cast('6.95' as decimal(10,2)) + cast('-55.80' as decimal(10,2)) y, cast('48.85' as decimal(10,2)) + cast('6.95' as decimal(10,2)) + cast('-55.80' as decimal(10,2)) z """).show()
执行后y、z列都会返回精确的0.00。
方案2:对Double结果做四舍五入(临时处理)
如果业务场景必须使用Double类型,可以在运算后用round函数保留指定小数位,比如:
spark.sql(""" select 48.85 + 6.95 + -55.80 x, round('48.85' + '6.95' + '-55.80', 2) y, round(cast('48.85' as double) + cast('6.95' as double) + cast('-55.80' as double), 2) z """).show()
这种方式是对近似值做修正,适合对精度要求不高的场景,但不如Decimal从根源上解决问题。
补充说明
这个不是Spark的bug,而是所有遵循IEEE 754标准的浮点数系统都会存在的特性。Spark 2.4.4的编译期优化恰好让字面量运算规避了这个问题,而运行时的Double运算则暴露了浮点数的本质限制。
内容的提问来源于stack exchange,提问作者user14072884
相关产品推荐
相关产品推荐

