为什么相同的代码在 32 位和 64 位机器上会产生不同的数值结果?

发布于 2024-12-11 07:31:35 字数 754 浏览 0 评论 0原文

我们正在开发一个 C 语言的数字例程库。我们还不确定是否会使用单精度 (float) 还是双精度 (double),因此我们'我们定义了一个类型 SP 作为别名,直到我们决定:

typedef float SP;

当我们运行单元测试时,它们都在我的机器(64 位 Ubuntu)上通过,但在我同事的机器(32 位)上失败Ubuntu 被错误地安装在64 位机器)。

使用 Git 的 bisect 命令,我们发现了在他的机器和我的机器之间开始产生不同结果的确切差异:

-typedef double SP;
+typedef float SP;

换句话说,从双精度到单精度在我们的机器上产生数值上不同的结果(大约 1e- 3 最坏情况下的相对差异)。

我们相当确定,我们永远不会在任何地方将无符号整数与负符号整数进行比较。

为什么数字例程库在 32 位操作系统和 64 位系统上会产生不同的结果?

澄清

恐怕我可能没有说清楚足够了:我们有使用双精度的 Git 提交 2f3f671,并且单元测试在两台机器上同样顺利通过。然后我们有 Git 提交 46f2ba,我们将其更改为单精度,这里测试仍然在 64 位机器上通过,但不能 32 位机器。

We are working on a library of numeric routines in C. We are not sure yet whether we will work with single precision (float) or double (double), so we've defined a type SP as an alias until we decide:

typedef float SP;

When we run our unit tests, they all pass on my machine (a 64-bit Ubuntu) but they fail on my colleague's (a 32-bit Ubuntu that was mistakenly installed on a 64-bit machine).

Using Git's bisect command, we found the exact diff that began yielding different results between his machine and mine:

-typedef double SP;
+typedef float SP;

In other words, going from double precision to single precision yields numerically different results on our machines (about 1e-3 relative difference in the worst cases).

We are fairly certain that we are never comparing unsigned ints to negative signed ints anywhere.

Why would a library of numeric routines yield different results on a 32-bit operating system and on a 64-bit system?

CLARIFICATION

I'm afraid I might not have been clear enough: we have Git commit 2f3f671 that uses double precision, and where the unit tests pass equally well on both machines. Then we have Git commit 46f2ba, where we changed to single precision, and here the tests still pass on the 64-bit machine but not on the 32-bit machine.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

路还长,别太狂 2024-12-18 07:31:35

您遇到了通常所说的“x87 超精度“bug””。

简而言之:历史上,(几乎)x86 处理器上的所有浮点计算都是使用 x87 指令集完成的,该指令集默认在 80 位浮点类型上运行,但可以设置为以单精度或双精度运行-控制寄存器中某些位的精度(几乎)。

如果在 x87 控制寄存器的精度设置为双精度或扩展精度时执行单精度运算,则结果将与以单精度执行相同运算时产生的结果不同(除非编译器非常小心,存储每次计算的结果并重新加载它以强制舍入发生在正确的位置。)

在 32 位上运行的代码正在使用 x87 单元进行浮点计算(显然控制寄存器设置为双精度),从而遇到上述问题。在 64 位上运行的代码使用 SSE[2,3,...] 指令进行浮点计算,该指令提供本机单精度和双精度运算,因此不会携带超额精度。这就是您的结果不同的原因。

您可以通过告诉编译器使用 SSE 进行浮点计算(甚至在 32 位上)(使用 GCC 的 -mfpmath=sse)来解决此问题(在某种程度上)。即使这样,也不能保证位精确的结果,因为您链接的各种库可能使用 x87,或者只是根据架构使用不同的算法。

You are encountering what is often called the 'x87 excess-precision "bug"'.

In short: historically, (nearly) all floating-point computation on x86 processors was done using the x87 instruction set, which by default operates on an 80-bit floating-point type, but can be set to operate in either single- or double-precision (almost) by some bits in a control register.

If single-precision operations are performed while the precision of the x87 control register is set to double- or extended-precision, then the results will differ from what would be produced if the same operations were performed in single-precision (unless the compiler is extraordinarily careful and stores the result of every computation and reloads it to force rounding to occur in the correct place.)

Your code running on 32-bit is using the x87 unit for floating-point computation (apparently with the control register set for double-precision), and thus encountering the issue described above. Your code running on 64-bit is using the SSE[2,3,...] instructions for floating-point computation, which provide native single- and double-precision operations, and therefore does not carry excess-precision. This is why your results differ.

You can work around this (to a point) by telling your compiler to use SSE for floating-point computation even on 32-bit (-mfpmath=sse with GCC). Even then, bit-exact results are not guaranteed because the various libraries that you link against may use x87, or simply use different algorithms depending on the architecture.

不再让梦枯萎 2024-12-18 07:31:35

IIRC,“double”的精度只需要求为“float”的>=精度。因此,在一种实现中,“float”和“double”中的实际位数可能相同,而在另一种实现中,它们不同。这可能与平台中的 32 位/64 位差异有关,但也可能不是。

IIRC, the precision of 'double' is only required to be >= the precision of 'float'. So on one implementation the actual number of bits in 'float' and 'double' may be the same, while on the other they differ. This is probably realted to the 32-bit/64-bit difference in the platforms, but may not be.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文