我正在将一些程序从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 ++中都是不同的)所以最终的差异是巨大的)(如果你很好奇,看看差异如何开始变大,这里是完整输出[很快链接],但这与问题无关)
答案 0 :(得分:5)
首先,如果您的数值方法取决于sin到最后一位的精度,那么您可能需要使用任意精度库,例如MPFR。
IEEE754 2008标准并不要求对函数进行正确舍入(尽管如此,它会推荐#34;它)。一些C libms确实提供了正确的舍入三角函数:我相信glibc libm(通常用于大多数Linux发行版),就像CRlibm一样。大多数其他现代libms将提供1 ulp以内的触发函数(即真值两侧的两个浮点值之一),通常称为忠实舍入,计算速度更快。 / p>
您打印的这些值中没有一个实际上可以作为IEEE 64位浮点值出现(即使是舍入的):最接近的3(打印到全精度)是:
0.477158760259608 405451814405751065351068973541259765625
0.477158760259608 46096296563700889237225055694580078125
0.477158760259608 516474116868266719393432140350341796875
您可能需要的值是:
0.477158760259608 433132061388630377105954125778369485736356219 ...
(这似乎是Mathematica给出的)。
0.477158760259608 430531153841011107415427334794384396325832953 ...
在这两种情况下,上面列表中的第一个是最近的(尽管只有1的情况)。
答案 1 :(得分:1)
你写的double
常数的正弦值约为0x1.e89c4e59427b173a8753edbcb95p-2,其最近的double
为0x1.e89c4e59427b1p-2。小数点后20位,最接近double
的两个是0.47715876025960840545和0.47715876025960846096。
也许Matlab显示的是截断值? (编辑:我现在看到第四个数字是6,而不是0.Matlab给你的结果仍然忠实地舍入,但它是最接近期望结果的两个double
的距离它仍然打印出错误的数字。
我还应该指出,Mathematica可能正在尝试解决另一个问题 - 计算十进制数0.497418836818383950的正弦值到20位小数。您不应期望这与C ++代码的结果或Matlab的结果相匹配。