acos(double)在x64和x32 ​​Visual Studio

时间:2017-08-17 11:59:02

标签: c++ visual-studio double precision sse

acos(double)在x64和x32 ​​Visual Studio上给出了不同的结果。

printf("%.30g\n", double(acosl(0.49990774364240564)));
printf("%.30g\n", acos(0.49990774364240564));
x64上的

1.0473040763868076
在x32:1.0473040763868078

在启用了sse的linux4.4 x32和x64上

1.0473040763868078

有没有办法让VSx64 acos()给我1.0473040763868078作为结果?

3 个答案:

答案 0 :(得分:2)

您可能已达到精确度限制。 Double precision is approximately 16 digits。之后,无法保证数字有效。如果是这样,除了将double类型更改为其他内容之外,您无法更改此行为,从而支持更高的精度。

E.g。如果您的机器和编译器支持extended 80 bit double或128位Quadruple(也可以依赖于机器,请参见here),则加倍。

答案 1 :(得分:2)

TL:DR:这是正常的,你不能合理地改变它。

32位库可能在x87寄存器中使用80位FP值作为临时值,避免在每次操作后四舍五入为double。 (除非有一个完整的独立库,编译自己的代码以使用SSE不会改变库中的内容,甚至不会改变将数据传递给库的调用约定。但是因为32位传递double和{{在堆栈的内存中,库可以使用SSE2或x87自由加载它。但是,除非非SSE代码不可能使用,否则你不会获得在xmm寄存器中传递FP值的性能优势。库)。

它们也可能因为使用不同的操作顺序而产生不同,从而产生不同的临时性。这不太合理,除非它们是用asm单独手写的。如果它们是使用相同的C源构建的(没有“不安全的”FP优化),则不允许编译器重新排序,因为FP数学的这种非关联行为。

glibc的libm(在Linux上使用)通常倾向于精确超速,因此它为32位和64位的尾数的最后一位提供了正确的舍入结果。 IEEE FP标准仅要求将基本操作(+ - * / FMA和FP余数)“正确舍入”到尾数的最后一位。 (即最多为0.5 ulp的舍入误差)。 (根据calc,确切的结果是float。请记住1.047304076386807714...(在x86上使用普通编译器)是IEEE754 binary64,因此尾数和指数内部都是double在base2。如果你打印了足够的额外十进制数字,你可以告诉...7714应该向上舍入到...78,虽然你应该打印更多数字,以防它们不是零。我我只是假设它是...78000。)

因此,Microsoft的64位库实现会生成1.0473040763868076,除了不使用它之外,你几乎无能为力。。 (例如找到你自己的acos()实现并使用它。)但是FP确定性是 hard ,即使你用SSE将自己限制在x86。见Does any floating point-intensive code produce bit-exact results in any x86-based architecture?。如果您将自己局限于单个编译器,则可以避免使用acos()之类的复杂库函数。

如果使用x87并且更改x87精度设置会影响它,您可能能够使32位库版本生成与64位版本相同的值。但另一种方法是不可能的:SSE2有64位double和32位float的单独指令,并且总是在每条指令后舍入,所以你不能改变任何会增加的设置精确的结果。 (您可以更改SSE舍入模式,这将改变结果,但不是很好!)

另见:

答案 2 :(得分:1)

我不同意你无能为力。

例如,您可以尝试更改浮点模型编译器选项。

以下是我使用不同浮点模型的结果(注意/fp:precise是默认值):

/fp:precise   1.04730407638680755866289473488
/fp:strict    1.04730407638680755866289473488
/fp:fast      1.04730407638680778070749965991

所以看来你正在寻找/fp:fast。是否能提供最准确的结果仍有待观察。