不同语言的浮点精度
Floating point accuracy with different languages
我目前正在计算坐标之间的距离,根据使用的语言,我得到的结果略有不同。
计算的一部分是计算给定 radian
的 cosine
。我得到以下结果
// 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
我想尝试了解原因。如果你看过去 16dp
c
是唯一“正确”的四舍五入答案。令我惊讶的是 python
结果不同。
这个微小的差异目前正在被放大,超过 000 个位置正在增加一个不小的距离。
不太确定这是怎么重复的。此外,我更多地要求一个整体的答案,而不是特定的语言。我没有计算机科学学位。
更新
我承认这可能是一个太宽泛的问题,我想我很好奇为什么我的背景不是 CS。我很欣赏评论中发布的博客链接。
更新 2
这个问题源于将服务从 nodejs
移植到 go
。 Go
甚至更奇怪,因为我现在无法 运行 测试,因为距离的总和随多个值而变化。
给定坐标列表并计算距离并将它们相加,我得到不同的结果。我不是在问问题,但 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
}
通常,浮点数的表示由标准 IEEE 754 定义,我的假设是所有(主要)编程语言都实现了该标准。
精度和舍入为known issues and may sometimes lead to unexpected results。
根据编程语言或使用的数学库,可能对计算结果产生影响的方面:
- 不同的计算方法(在你的例子中:余弦函数
可能由 numerical approximation 使用不同的方法实施)
- 不同 rounding strategies 在计算期间或最终输出
IEEE-754只要求基本运算(+-*/
)和sqrt
正确舍入,即误差不得超过0.5ULP。像 sin
、cos
、exp
... 这样的超越函数非常复杂,所以它们只有 recommended to be properly rounded。不同的实现可能会使用不同的算法来计算这些函数的结果,具体取决于 space 和时间要求。因此,像您观察到的变化是完全正常的
There is no standard that requires faithful rounding of transcendental functions. IEEE-754 (2008) recommends, but does not require, that these functions be correctly rounded.
Standard for the sine of very large numbers
另见
- Math precision requirements of C and C++ standard
- Does any floating point-intensive code produce bit-exact results in any x86-based architecture?
我目前正在计算坐标之间的距离,根据使用的语言,我得到的结果略有不同。
计算的一部分是计算给定 radian
的 cosine
。我得到以下结果
// 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
我想尝试了解原因。如果你看过去 16dp
c
是唯一“正确”的四舍五入答案。令我惊讶的是 python
结果不同。
这个微小的差异目前正在被放大,超过 000 个位置正在增加一个不小的距离。
不太确定这是怎么重复的。此外,我更多地要求一个整体的答案,而不是特定的语言。我没有计算机科学学位。
更新
我承认这可能是一个太宽泛的问题,我想我很好奇为什么我的背景不是 CS。我很欣赏评论中发布的博客链接。
更新 2
这个问题源于将服务从 nodejs
移植到 go
。 Go
甚至更奇怪,因为我现在无法 运行 测试,因为距离的总和随多个值而变化。
给定坐标列表并计算距离并将它们相加,我得到不同的结果。我不是在问问题,但 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
}
通常,浮点数的表示由标准 IEEE 754 定义,我的假设是所有(主要)编程语言都实现了该标准。
精度和舍入为known issues and may sometimes lead to unexpected results。
根据编程语言或使用的数学库,可能对计算结果产生影响的方面:
- 不同的计算方法(在你的例子中:余弦函数 可能由 numerical approximation 使用不同的方法实施)
- 不同 rounding strategies 在计算期间或最终输出
IEEE-754只要求基本运算(+-*/
)和sqrt
正确舍入,即误差不得超过0.5ULP。像 sin
、cos
、exp
... 这样的超越函数非常复杂,所以它们只有 recommended to be properly rounded。不同的实现可能会使用不同的算法来计算这些函数的结果,具体取决于 space 和时间要求。因此,像您观察到的变化是完全正常的
There is no standard that requires faithful rounding of transcendental functions. IEEE-754 (2008) recommends, but does not require, that these functions be correctly rounded.
Standard for the sine of very large numbers
另见
- Math precision requirements of C and C++ standard
- Does any floating point-intensive code produce bit-exact results in any x86-based architecture?