浮点相等性测试和额外精度:这段代码会失败吗?
Floating-point equality test and extra precision: can this code fail?
讨论从我对另一个问题的回答开始。以下代码确定机器epsilon:
float compute_eps() {
float eps = 1.0f;
while (1.0f + eps != 1.0f)
eps /= 2.0f;
return eps;
}
在注释中,有人提出1.0f + eps != 1.0f
测试可能会失败,因为c++标准允许使用额外的精度。虽然我知道浮点操作实际上以更高的精度执行(比实际使用的类型指定的精度更高),但我碰巧不同意这个建议。
我怀疑在比较运算期间,如==
或!=
,操作数没有被截断到其类型的精度。换句话说,1.0f + eps
当然可以以比float
(例如long double
)更高的精度求值,并且结果将存储在可以容纳long double
的寄存器中。然而,我认为在执行!=
操作之前,左操作数将从long double
截断到float
,因此代码永远不会无法精确地确定eps
(即它永远不会做比预期更多的迭代)。
我在c++标准中没有找到关于这个特殊情况的任何线索。此外,代码工作得很好,我确信在执行过程中使用了额外的精度技术,因为我毫不怀疑任何现代桌面实现实际上在计算过程中使用了额外的精度。
你觉得怎么样?
对不起,这个例子是C而不是c++。这应该不难适应:
~ $ gcc -mfpmath=387 -mno-sse2 c.c
~ $ ./a.out
incredible but true.
~ $ gcc -mfpmath=sse -msse2 c.c
~ $ ./a.out
~ $ cat c.c
#include "stdio.h"
double d = 3. / 7.;
double d1 = 3.;
int main() {
if (d != d1 / 7.)
printf("incredible but true.n");
return 0;
}
gcc -msse2 -mfpmath=sse
是严格的IEEE 754编译器。使用该编译器,if
永远不会被占用。然而,gcc -mno-sse2 -mfpmath=387
必须使用精度更高的387单元。在!=
测试之前,它不会降低精度。最后对3的扩展精度结果进行了比较。/7。右手边是左手边相同除法的双精度结果。这将导致可能出现奇怪的行为。
gcc -msse2 -mfpmath=sse
和gcc -mno-sse2 -mfpmath=387
都符合标准。只是前者很容易生成SSE2指令,因此可以提供严格的IEEE 754实现,而后者必须使用古老的指令集。
循环,如:
while (eps1 != 1.0f)
eps /= 2.0f, eps1 = 1.0f + eps;
在float
类型下声明eps1
的在扩展精度方面应该更健壮。
生成在比较前不截断的x87代码的编译器是:
~ $ gcc -v
Using built-in specs.
Target: i686-apple-darwin11
Configured with: /private/var/tmp/llvmgcc42/llvmgcc42-2336.11~148/src/configure --disable-checking --enable-werror --prefix=/Applications/Xcode.app/Contents/Developer/usr/llvm-gcc-4.2 --mandir=/share/man --enable-languages=c,objc,c++,obj-c++ --program-prefix=llvm- --program-transform-name=/^[cg][^.-]*$/s/$/-4.2/ --with-slibdir=/usr/lib --build=i686-apple-darwin11 --enable-llvm=/private/var/tmp/llvmgcc42/llvmgcc42-2336.11~148/dst-llvmCore/Developer/usr/local --program-prefix=i686-apple-darwin11- --host=x86_64-apple-darwin11 --target=i686-apple-darwin11 --with-gxx-include-dir=/usr/include/c++/4.2.1
Thread model: posix
gcc version 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2336.11.00)
下面是另一个:
~ $ clang -mno-sse2 c.c
~ $ ./a.out
incredible but true.
~ $ clang -v
Apple LLVM version 4.2 (clang-425.0.24) (based on LLVM 3.2svn)
Target: x86_64-apple-darwin12.3.0
Thread model: posix
- 在java中解决这段代码时面临循环中的问题
- 修改 VS Code 中的默认C++代码段
- 我是如何在这段代码中出现分段错误的
- 我不明白这段代码是如何对这个pythonlist()进行排序的,也不明白如何用C++中的向量来重现它
- 为什么这段代码不起作用,我该如何解决?
- 为什么这段代码给我错误? 有没有自错?
- 有人可以解释一下这段代码如何能够反转字符串
- 可能我知道为什么这段代码没有给出任何输出吗?
- 这段代码的最后一行在做什么?
- 我不知道为什么这段代码会让核心被转储?
- 我试图用这段代码找到二叉树的高度,但它一直返回 0,有人可以告诉我为什么吗?
- 有人可以向我解释一下这段代码的作用吗?
- 存储在哪个内存段(代码/数据段)类(员工)中?
- 为什么这段代码会导致无限循环?
- 为什么代码段会引发浮点异常?
- 任何人都可以弄清楚这段代码如何显示运行错误?它打印无限时间 -1 以及正确答案
- 如何为一段代码启用 -permissive
- 如何知道用于实现标准代码段的确切数据结构和算法,例如在C++STL中?
- 为什么下面的代码段返回指针指向的值而不是指针的地址?
- 谁能告诉我为什么这段代码没有产生正确的输出?