浮点相等测试和额外精度:这个代码可以失败吗? [英] Floating-point equality test and extra precision: can this code fail?

查看:211
本文介绍了浮点相等测试和额外精度:这个代码可以失败吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

讨论开始于我对另一个问题的回答。以下代码确定 机器epsilon

The discussion started under my answer to another question. The following code determines machine 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 ++标准允许使用额外的精度。虽然我知道浮点运算实际上是以更高的精度(而不是由实际使用的类型指定)执行,我碰巧不同意这个建议。

In the comments it was proposed that the 1.0f + eps != 1.0f test might fail because C++ standard permits the use of extra precision. Although I'm aware that floating-point operations are actually performed in higher precision (than specified by the actual types used), I happen to disagree with this proposal.

I怀疑在比较操作期间,例如 == != ,操作数不会截断到他们的类型。换句话说, 1.0f + eps 当然可以用高于 float 的精度来求值(例如, code> long double ),结果将存储在可以容纳 long double 的寄存器中。但是,我认为在执行!= 操作左操作数将从 long double 截断到 float ,因此代码永远不能无法确定 eps (即它永远不能做比预期更多的迭代)。

I doubt that during the comparison operations, such as == or !=, the operands are not truncated to the precision of their type. In other words, 1.0f + eps can of course be evaluated with the precision higher than float (for example, long double), and the result will be stored in the register that can accommodate long double. However, I think that before performing the != operation left operand will be truncated from long double to float, hence the code can never fail to determine eps precisely (i.e. it can never do more iterations than intended).

我在C ++标准中没有找到任何关于这种特殊情况的线索。此外,代码工作正常,我确信在执行过程中使用了额外的精度技术,因为我毫不怀疑任何现代桌面实现在计算过程中实际上使用额外的精度。

I haven't found any clue on this particular case in C++ standard. Furthermore, the code works fine and I'm sure that the extra precision technique is used during its execution because I have no doubt that any modern desktop implementation in fact uses extra precision during calculations.

你觉得怎么样?

推荐答案

很抱歉,这个范例是C而不是C ++。不应该很难适应:

Sorry that this example is C and not C++. It should not be difficult to adapt:

~ $ 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编译器。使用该编译器,如果从不采取。但是, gcc -mno-sse2 -mfpmath = 387 必须使用具有更高精度的387单位。在!= 测试之前,它不会降低精度。测试结束时将右侧的3. / 7.的扩展精度结果与左侧相同除法的双精度结果进行比较。

gcc -msse2 -mfpmath=sse is a strict IEEE 754 compiler. With that compiler, the if is never taken. However, gcc -mno-sse2 -mfpmath=387 has to use the 387 unit with its higher precision. It does not reduce the precision before the != test. The test ends up comparing the extended-precision result of 3. / 7. on the right-hand side to the double-precision result of the same division on the left-hand side. This cause a behavior that may appear strange.

gcc -msse2 -mfpmath = sse gcc -mno-sse2 -mfpmath = 387 是标准兼容的。只有前者容易,产生SSE2指令,因此可以提供严格的IEEE 754实现,而后者必须用古老的指令集来完成它。

Both gcc -msse2 -mfpmath=sse and gcc -mno-sse2 -mfpmath=387 are standard-compliant. It is only the case that the former has it easy, generating SSE2 instructions, and thus can provide a strict IEEE 754 implementation, whereas the latter has to do its best with an ancient instruction set.

一个循环,例如:

while (eps1 != 1.0f)
  eps /= 2.0f, eps1 = 1.0f + eps;

eps1 声明类型 float 在扩展精度上应该更加健壮。

with eps1 declared of type float should be more robust with respect to extended precision.

编译器生成x87代码,在比较之前不会截断此代码:

The compiler that generates x87 code that does not truncate before comparison is this one:

~ $ 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

这篇关于浮点相等测试和额外精度:这个代码可以失败吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆