不同语言的浮点精度

Floating point accuracy with different languages

提问人:amwill04 提问时间:10/16/2019 最后编辑:phuclvamwill04 更新时间:8/2/2022 访问量:961

问:

我目前正在进行坐标之间的距离计算,并且根据所使用的语言得到的结果略有不同。

计算的一部分是计算给定的 .我得到以下结果cosineradian

// cos(0.8941658257446736)

// 0.6261694290123146 node
// 0.6261694290123146 rust
// 0.6261694290123148 go
// 0.6261694290123148 python
// 0.6261694290123148 swift
// 0.6261694290123146 c++
// 0.6261694290123146 java
// 0.6261694290123147 c

我想试着理解为什么。如果你看过去,就四舍五入而言是唯一“正确”的答案。令我惊讶的是,结果却有所不同。16dpcpython

这个微小的差异目前正在被放大,超过 000 个位置它正在增加一个不小的距离。

不太确定这是怎么回事。此外,我更要求一个整体的答案,而不是具体的语言。我没有计算科学学位。


更新

我承认,也许这是一个过于宽泛的问题,我想我很好奇为什么因为我的背景不是 CS。我很欣赏评论中发布的博客链接。


更新 2

这个问题源于将服务从 移植到 。 更奇怪的是,我现在无法运行测试,因为距离的总和随多个值而变化。nodejsgoGo

给定一个坐标列表并计算距离并将它们相加,我得到了不同的结果。我不是在问问题,但看起来很疯狂,会产生不同的结果。go

9605.795975874069
9605.795975874067
9605.79597587407

为了完整起见,以下是我使用的距离计算:

func Distance(pointA Coordinate, pointB Coordinate) float64 {
    const R = 6371000 // Earth radius meters
    phi1 := pointA.Lat * math.Pi / 180
    phi2 := pointB.Lat * math.Pi / 180
    lambda1 := pointA.Lon * math.Pi / 180
    lambda2 := pointB.Lon * math.Pi / 180

    deltaPhi := phi2 - phi1
    deltaLambda := lambda2 - lambda1
    a := math.Sin(deltaPhi/2)*math.Sin(deltaPhi/2) + math.Cos(phi1)*math.Cos(phi2)*math.Sin(deltaLambda/2)*math.Sin(deltaLambda/2)
    c := 2 * math.Atan2(math.Sqrt(a), math.Sqrt(1-a))

    d := R * c
    return d
}
C++ 浮点 语言不可知 精度 超越方程

评论

16赞 icza 10/16/2019
您发布的结果是以某种方式显示给您的结果。这可能在某种程度上涉及舍入和截断,因语言和打印方式而异。例如,Go 的输出是 ,但如果你使用“更高精度”的打印,比如 ,你会得到 .第 16 个小数数字与使用默认格式打印时不进行比较。0.6261694290123148fmt.Printf("%.25f", v)0.626169429012314759930291178
8赞 Richard Critten 10/16/2019
双精度和精度的可能重复 您正处于边缘,这将取决于如何实现以及是否将 80 位浮点用于中间结果。cos
6赞 GhostCat 10/16/2019
基本上,你在一个问题中问了 10 个问题(比如“X 在语言 Y 中做什么”,针对 10 种不同的语言)。另外:告诉我们你认为你的代码是做什么的......是不够的。请参阅最小可重现示例。应该如何告诉您可能遇到什么样的 FP 问题,而无法查看创建其中一行的确切代码?!
1赞 amwill04 10/16/2019
复制起来并不难,只需计算一下,每个语言都需要确切的语法??这是为每种语言使用内置函数,所以不明白你想要什么。我问了一个更全面的问题,就像@icza所做的那样cosine
4赞 Max Langhof 10/16/2019
有趣的事实:在 C++ 中,触发的可能性很大(一个可能在编译时计算,另一个在运行时计算,可能使用不同的方法)。请参阅 randomascii.wordpress.com/2013/07/16/floating-point-determinism 中的“超验”部分。所以是的,使用的确切语法(和编译器、标志和浮点舍入模式等)可能是相关的。x = 0.8941658257446736; assert(cos(x) == cos(x + argc/1000));

答:

1赞 Gerd 10/16/2019 #1

通常,浮点数的表示由标准 IEEE 754 定义,我的假设是该标准由所有(主要)编程语言实现。

精度和舍入是已知问题,有时可能会导致意外结果

根据编程语言或使用的数学库,可能会对计算结果产生影响的方面:

  • 不同的计算方法(在您的例子中:余弦函数 可以通过不同方法的数值近似来实现)
  • 在计算期间或最终输出时采用不同的舍入策略

评论

0赞 Marek R 8/2/2022
这不是语言问题,而是硬件(CPU 和 FPU 设计)。
0赞 MSalters 8/2/2022
@MarekR:硬件可以使IEEE754更容易或更难实现,但你可以完全在软件中完成IEEE754。
2赞 phuclv 8/19/2020 #2

IEEE-754 只需要基本运算 () 并正确舍入,即误差不得超过 0.5ULP。先验功能,如 、 、 ...非常复杂,因此只建议正确舍入。不同的实现可能会使用不同的算法来计算这些函数的结果,具体取决于空间和时间要求。因此,像您观察到的变化是完全正常的+-*/sqrtsincosexp

没有一个标准要求忠实地四舍五入先验功能。IEEE-754 (2008) 建议(但不要求)正确舍入这些函数。

非常大数的正弦波的标准

另请参阅

评论

1赞 Eric Postpischil 8/20/2020
“正确舍入”和“忠实舍入”是浮点数中的特定术语,需要谨慎使用。“适当四舍五入”不是一个特别定义的短语;这将取决于上下文中“适当”的含义。“正确”舍入是最严格的:浮点结果必须是无限精确的结果,四舍五入到由舍入规则唯一确定的数字。这并不等同于最多 1/2 ULP 的四舍五入,因为必须遵守平局规则。“忠实”则更宽松:结果可能是紧邻的两个邻居之一。
0赞 Eric Postpischil 8/20/2020
“仅建议适当舍入”一语在“建议”附近有“仅”,尽管它更可能是为了修饰“适当四舍五入”。
2赞 Eric Postpischil 8/20/2020
同样值得注意的是,人类还不知道如何在已知的有界执行时间内以正确的舍入来计算所有“基本”函数(正弦、余弦、对数等)。如果没有新的研究,没有人能够实现满足 IEEE 754 建议的库,并且执行时间有已知的限制。在我的建议下,委员会投票建议忠实地进行四舍五入,但在委员会完成工作后,投票方式发生了变化。