`std::sin` 最后一点是错误的

`std::sin` is wrong in the last bit

我正在将一些程序从 Matlab 移植到 C++ 以提高效率。两个程序的输出完全相同很重要 (**)。

我面临此操作的不同结果:

std::sin(0.497418836818383950)   = 0.477158760259608410 (C++)
sin(0.497418836818383950)        = 0.47715876025960846000 (Matlab)
N[Sin[0.497418836818383950], 20] = 0.477158760259608433 (Mathematica)

因此,据我所知,C++ 和 Matlab 都在使用 IEEE754 定义的双精度算法。我想我已经在某处读到 IEEE754 在最后一位允许不同的结果。用mathematica来判断,好像C++更接近结果。 如何强制 Matlab 计算 sin 时精确到最后一位,以便结果相同?

在我的程序中,这种行为会导致很大的错误,因为数值微分方程求解器在最后一位不断增加此错误。但是我不确定 C++ 移植版本是否正确。我猜 即使 IEEE754 允许最后一位不同,但在更多 IEEE754 定义的双精度运算中使用结果时,以某种方式保证这个错误不会变得更大 (因为否则,两个根据 IEEE754 标准正确的不同程序可能会产生完全不同的输出)。所以另一个问题是 我说得对吗?

我想得到两个粗体问题的答案。 编辑:第一个问题比较有争议,但不是很重要,有人可以评论一下第二个吗?

注意:这不是打印错误,以防万一你想检查,我是这样得到这些结果的:

http://i.imgur.com/cy5ToYy.png

注意(**):我的意思是最终输出,即一些计算结果,显示一些小数点后4位的实数,需要完全相同。我在问题中谈到的错误变得更大(因为更多的操作,每一个在 Matlab 和 C++ 中都是不同的)所以最终的差异是巨大的)(如果你有足够的好奇心想看看差异是如何开始变大的,这里是完整的输出[link soon],但这与问题无关)

你写的double常数的正弦值大约是0x1.e89c4e59427b173a8753edbcb95p-2,最接近的double是0x1.e89c4e59427b1p-2。到小数点后20位,最接近的两个double是0.47715876025960840545和0.47715876025960846096.

也许 Matlab 显示的是截断值? (编辑:我现在看到倒数第四个数字是 6,而不是 0。Matlab 给你的结果仍然是忠实的四舍五入,但它是离期望结果最近的两个 doubles 中较远的一个.而且它仍然打印出错误的数字。

我还应该指出,Mathematica 可能正在尝试解决一个不同的问题---计算小数 0.497418836818383950 的正弦到小数点后 20 位。您不应期望它与 C++ 代码的结果或 Matlab 的结果相匹配。

首先,如果您的数值方法依赖于 sin 到最后一位的精度,那么您可能需要使用任意精度库,例如 MPFR。

IEEE754 2008 标准不要求正确舍入函数(但 "recommend" 确实如此)。一些 C libms 确实提供了正确舍入的三角函数:我相信 glibc libm 可以(通常用于大多数 linux 发行版),CRlibm 也是如此。大多数其他现代 libms 将提供 1 ulp 以内的三角函数(即真值两侧的两个浮点值之一),通常称为 忠实舍入 ,这要快得多计算。

您打印的这些值中的

None 实际上可能作为 IEEE 64 位浮点值出现(即使四舍五入):最接近的 3 个(打印为全精度)是:

0.477158760259608 405451814405751065351068973541259765625

0.477158760259608 46096296563700889237225055694580078125

0.477158760259608 516474116868266719393432140350341796875

您可能需要的可能值是:

  1. 小数的正弦.497418836818383950,即

0.477158760259608 433132061388630377105954125778369485736356219...

(这似乎是 Mathematica 给出的)。

  1. 最接近 .497418836818383950 的 64 位浮点数的精确值:

0.477158760259608 430531153841011107415427334794384396325832953...

在这两种情况下,上面列表中的第一个是最近的(尽管在 1 的情况下只是勉强)。