从 i386 移动到 x86_64 时的浮点精度
Posted
技术标签:
【中文标题】从 i386 移动到 x86_64 时的浮点精度【英文标题】:Floating-point precision when moving from i386 to x86_64 【发布时间】:2008-11-27 02:26:40 【问题描述】:我有一个为 Linux x86 32 位开发的应用程序。根据结果,有很多浮点运算和大量测试。现在我们将它移植到x86_64,但是在这个架构下测试结果是不同的。我们不想为每个架构保留一组单独的结果。
根据文章 An Introduction to GCC - for the GNU compilers gcc and g++,问题是 X86_64 中的 GCC 假定 fpmath=sse 而 x86 假定 fpmath=387。 387 FPU 对所有操作使用80 位内部精度,并且只将结果转换为给定的浮点类型(float、double 或 long double),而 SSE 使用操作数的类型来确定其内部精度。
我可以在编译自己的代码时强制 -mfpmath=387 并且我的所有操作都正常工作,但是每当我调用某些库函数(sin、cos、atan2 等)时,结果都是错误的再次。我认为这是因为 libm 是在没有 fpmath 覆盖的情况下编译的。
我尝试使用 387 仿真自己构建 libm (glibc),但它导致了很多崩溃(不知道我是否做错了什么)。
有没有办法强制进程中的所有代码使用 x86_64 中的 387 仿真?或者也许一些库在两种架构上都返回与 libm 相同的值?有什么建议吗?
关于“你需要80位精度”的问题,我不得不说这对于个人操作来说不是问题。在这种简单的情况下,差异非常小,没有任何区别。但是,当复合大量操作时,错误会传播,最终结果的差异不再那么小,并且会产生影响。所以我想我需要 80 位精度。
【问题讨论】:
【参考方案1】:我会说你需要修复你的测试。如果您假设浮点数学是准确的,那么您通常会让自己失望。不要测试 exact 相等性,而是测试它是否足够接近预期结果。毕竟,您发现的不是错误,所以如果您的测试报告错误,测试 是错误的。 ;)
正如您所发现的,您所依赖的每个库都将采用 SSE 浮点,所以除非您打算手动编译 everything,现在和永远,这样您就可以设置 FP模式到 x87,你最好现在处理这个问题,并接受 FP 数学不是 100% 准确的,并且通常不会在两个不同的平台上产生相同的结果。 (我相信 AMD CPU 在 x87 数学运算中产生的结果也略有不同)。
您绝对需要 80 位精度吗? (如果是这样,除了自己编译 everything 以使用 80 位 FP 之外,显然没有太多选择。)
否则,请调整您的测试以在一些小 epsilon 内执行比较和相等测试。如果差值小于该 epsilon,则认为这些值相等。
【讨论】:
【参考方案2】:80 位精度实际上是危险的。问题在于,只要变量存储在 CPU 寄存器中,它实际上就会被保留。每当它被强制输出到 RAM 时,它都会被截断为类型精度。因此,即使代码中没有发生任何事情,您也可以让变量实际更改其值。
【讨论】:
【参考方案3】:如果您想要long double
精度,请对所有浮点变量使用long double
,而不是期望float
或double
具有额外的神奇精度。这真的很简单。
【讨论】:
完全正确。依赖扩展的内部精度只会导致心痛。 我一直在寻找一种不需要更改太多代码并将内存需求翻倍的解决方案。但是,正如您所说,这显然可以解决问题。【参考方案4】:SSE 浮点数和 387 浮点数使用完全不同的指令,因此无法说服 SSE fp 指令使用 387。处理此问题的最佳方法可能是让您的测试套件得到稍微不同的结果,并且不依赖于结果与最后一位相同。
【讨论】:
以上是关于从 i386 移动到 x86_64 时的浮点精度的主要内容,如果未能解决你的问题,请参考以下文章
如何在 x86_64 Swift 项目中使用 i386 框架
iOS 指令集arm64、armv7s、armv7、i386、x86_64