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
:1.0473040763868078
有没有办法让VSx64 acos()
给我1.0473040763868078
作为结果?
答案 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舍入模式,这将改变结果,但不是很好!)
另见:
Intermediate Floating-Point Precision以及Bruce Dawson关于浮点的优秀系列文章的其余部分。 (table of contents
链接文章描述了VC ++的某些版本的CRT运行时启动如何将x87 FP寄存器精度设置为53位尾数而不是80位全精度。此外,D3D9会将其设置为24,因此如果使用x87,即使double
也只有float
的精度。
答案 2 :(得分:1)
我不同意你无能为力。
例如,您可以尝试更改浮点模型编译器选项。
以下是我使用不同浮点模型的结果(注意/fp:precise
是默认值):
/fp:precise 1.04730407638680755866289473488
/fp:strict 1.04730407638680755866289473488
/fp:fast 1.04730407638680778070749965991
所以看来你正在寻找/fp:fast
。是否能提供最准确的结果仍有待观察。