控制 Spark 中的小数精度溢出

Ven*_*pan 5 decimal apache-spark apache-spark-sql

我们使用 Spark 2.4.x。

我们的除法运算之一存在精度损失,(69362.86 / 111862.86)这两个值在表中都定义为十进制(38,3)。当穿过直线时,它产生0.620070504187002,但当穿过火花时,它产生0.6200710。正如我们所看到的,spark 的结果有一个小数截断。阅读更多内容后,我们偶然发现了 Spark 故事SPARK-29123。注释要求我们设置参数spark.sql.decimalOperations.allowPrecisionLoss避免false精度损失。然而,同一个故事中还有另一个评论警告我们,当十进制值的精确表示不可能时,请注意 null。堆栈溢出线程没有讨论第二条评论中提到的警告。将此参数设置spark.sql.decimalOperations.allowPrecisionLossfalse并运行计算 (69362.86 / 111862.86) 结果0.620070504187002很好,但我们担心第二条评论中的警告。

根据源代码中规定的规则,除法精度和小数位数的值由以下公式确定。

Operation    Result Precision                        Result Scale
e1 / e2      p1 - s1 + s2 + max(6, s1 + p2 + 1)      max(6, s1 + p2 + 1)
Run Code Online (Sandbox Code Playgroud)

根据这些规则,我的精度是(38 -3 +3 + max(6,3 +38 +1)) => 80,规模是max(6,3 +38 +1) => 42。由于精度和小数位数均超过了默认限制 38,因此它们被减少为 38 和 6。修复此小数截断的一种方法是对输入列使用适当的小数精度和小数位数。我认为根据表中的数据,我们可以轻松地将参与除法的两列的输入精度设置为 18,将比例设置为 5。在这种情况下,最终的精度将为 38 和 24。这是足够好的精度和比例来表示我们的数据,而不会出现任何明显的截断。但我们无法对空间中的所有数字列手动执行此操作。所以我们正在考虑设置spark.sql.decimalOperations.allowPrecisionLossfalse集群级别。我们有兴趣了解更多有关当我们将此参数设置为 false 时结果将为 NULL 的情况的更多信息,但如果此参数保留为默认值,则会导致精度损失的值。

现在我的问题是,在什么情况下将此参数设置spark.sql.decimalOperations.allowPrecisionLossfalsenull,但将其保留为默认值(true)时,我们会得到一些带有精度损失的值。您能否提供任何我可以用来重现的示例?如果我们找不到这样的例子,那么我们是否可以在簇级别将此参数设置为 false,以便算术运算可以产生更好的结果?

Ven*_*pan 3

找到一些将此参数设置spark.sql.decimalOperations.allowPrecisionLosstruefalse产生不同结果的示例。我在下面举了两个这样的例子。

false从这个分析中,我了解到当这个参数设置为顾名思义时,小数值的小数部分没有容差。但是,如果算术运算结果的小数位数超过默认限制 38,则小数位数将减少到 38。对于小数值的整数部分,不进行检查,如果整数值在 (精度- scale) 则返回正确的值,否则计算返回 NULL。

因此,我们决定将此参数保留为默认值,true以避免出现小数列未定义得尽可能紧密地接近实际值的情况,因此算术运算结果为 NULL。

情况1:在此输入图像描述 案例2: 在此输入图像描述