[英]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
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.