【问题标题】:Neo4j floating point sum different resultsNeo4j 浮点求和不同的结果
【发布时间】:2019-10-29 03:51:48
【问题描述】:

我正在使用 neo4j 计算数据集的一些统计数据。为此,我经常在浮点值上使用 sum。根据情况,我得到不同的结果。例如,执行此操作的查询:

...
WITH foo
ORDER BY foo.fooId
RETURN SUM(foo.Weight)

返回与简单求和的查询不同的结果:

...
RETURN SUM(foo.Weight)

差异很小(293.07724195098984 vs 293.07724195099007)。但这足以使简单的相等检查失败。另一个例子是不同的数据库实例,使用相同的加载过程加载相同的数据会产生相同的问题(dbs 可能不是 1:1,某些关系的加载顺序可能不同)。我获取了 neo4j 求和的原始值(通过简单地删除 SUM())并验证它们在所有情况下都是相同的(不同的 dbs 和已排序/未排序)。

我在这里有什么选择?我不介意失去一些精度(我已经尝试将精度从小数点后 15 位降低到 12 位,但这似乎不起作用),但我需要匹配结果。

【问题讨论】:

    标签: neo4j floating-point sum precision


    【解决方案1】:

    由于舍入误差,浮点数不具有关联性。 (a+b)+c!=a+(b+c)。
    每个操作的结果都经过四舍五入以适应浮点编码约束,并且 (a+b)+c 实现为 round(round(a+b) +c) 而 a+(b+c) 实现为 round(a+round(b +c))。

    作为一个明显的说明,考虑操作 (2^-100 + 1 -1)。如果解释为 (2^-100 + 1)-1,它将返回 0,因为 1+2^-100 对于 IEEE754 中的浮点数或双重编码需要太大的精度,并且只能编码为 1.0。虽然 (2^-100 +(1-1)) 正确返回 2^-100,但可以用浮点数或双精度数编码。
    这是一个微不足道的例子,但这些舍入误差可能在每次运算后都存在,并解释了为什么浮点运算不具有关联性。

    数据库通常不会以保证的顺序返回数据,并且根据实际顺序,操作的执行方式会有所不同,这说明了您的行为。

    一般来说,出于这个原因,对浮点数进行相等比较并不是一个好主意。通常,建议将 a==b 替换为 abs(a-b) “足够”小。
    “足够”可能取决于您的算法。 float 相当于约 6-7 位小数,双倍于 15-16 位小数(我认为这是您的数据库中使用的)。根据计算次数,您可能会影响最后 1--3 位小数。
    最好的可能是使用
    abs(a-b) 必须根据您的问题调整相对误差。可能 10^-13 左右的值可能是正确的,但您必须进行实验,因为舍入误差取决于计算次数、值的分散以及您可能认为对您的问题“相等”的内容。

    查看this site 以讨论比较方法。并阅读 David Goldberg 的 What Every Computer Scientist Should Know About Floating-Point Arithmetic,其中讨论了这些问题等。

    【讨论】:

    • 感谢您的详细解释。最后我决定做的是将浮点数截断到小数点后 9 位,这似乎解决了我们的问题。我尝试了 12 但问题仍然出现在某些查询中。幸运的是,9 对我们的用例来说已经足够了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-31
    • 1970-01-01
    • 2013-08-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多