5

ディスカッションは、別の質問に対する私の回答の下で始まりました。次のコードは、マシンの epsilonを決定します。

float compute_eps() {
  float eps = 1.0f;

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

  return eps;
}

コメントでは、1.0f + eps != 1.0fC++ 標準では余分な精度の使用が許可されているため、テストが失敗する可能性があることが提案されました。浮動小数点演算が実際には (実際に使用される型で指定されているよりも) 高い精度で実行されることは承知していますが、たまたまこの提案には同意しません。

==orなどの比較演算中に!=、オペランドがその型の精度に切り捨てられないのではないかと思います。つまり1.0f + epsはもちろん よりも高い精度で評価することができfloat(例えばlong double)、結果は を収容できるレジスタに格納されlong doubleます。ただし、操作を実行する前に、左のオペランドが からに!=切り捨てられるため、コードが正確に判断できなくなることはありません (つまり、意図したよりも多くの反復を実行することはできません)。long doublefloateps

C++ 標準では、この特定のケースに関する手がかりは見つかりませんでした。さらに、コードは正常に動作し、実行中に特別な精度の手法が使用されていることは確かです。なぜなら、最近のデスクトップ実装では実際に計算中に特別な精度が使用されていることに疑いの余地がないからです。

あなたはそれについてどう思いますか?

4

1 に答える 1

4

申し訳ありませんが、この例は 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;

eps1型の宣言された withは、float拡張精度に関してより堅牢である必要があります。


比較前に切り捨てられない 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
于 2013-05-01T19:59:39.017 に答える