我们正在使用 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.allowPrecisionLoss
设置为 false
并运行计算 (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)
根据这些规则,我的精度是
(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.allowPrecisionLoss
设置为 false
。我们有兴趣了解更多有关当我们将此参数设置为 false 时结果将为 NULL 的情况的更多信息,但如果此参数保留为默认值,则会导致精度损失的值。
现在我的问题是,在什么情况下将此参数
spark.sql.decimalOperations.allowPrecisionLoss
设置为 false
会导致 null,但当将其保留为默认值(true)时,我们会得到一些带有精度损失的值。您能否提供任何我可以用来重现的示例?如果我们找不到这样的例子,我们是否可以在集群级别将此参数设置为 false,以便算术运算可以产生更好的结果?.
发现一些示例,将参数
spark.sql.decimalOperations.allowPrecisionLoss
设置为 true
或 false
会产生不同的结果。我在下面给出了 2 个这样的例子。
从这个分析中,我了解到,顾名思义,当这个参数设置为
false
时,小数值的小数部分没有容差。但是,如果算术运算结果的小数位数超过默认限制 38,则小数位数将减少到 38。对于小数值的整数部分,不进行检查,如果整数值在 (精度- scale) 则返回正确的值,否则计算返回 NULL。
因此,我们决定将此参数保留为其默认值
true
,以避免出现小数列未定义得尽可能紧贴实际值的情况,因此,算术运算结果为 NULL。
我们遇到了小数精度问题:有不同的方法,但根据我们的经验,最好的解决方案是避免它们。 转换为字符串,稍后需要时将使用 double 。 我们最初没有转换为双精度,因为对于巨大的表来说,它需要太多的内存,但首先转换为字符串(读取 DF),然后转换为十进制(就在保存之前)是最好的方法(仅将所需的列转换为双精度)