2

Computer Systems: A Programmer's Perspectiveという本を読んでいて、それが提供するコードを Intel Core i7 搭載の Macbook Pro で実行しようとしています。

しかし、一部のコードは、本が示唆するとおりには実行されません。

この C の例は、同じ float がメモリに格納される場合とレジスタに格納される場合とで異なることを示すことになっています。

#include<stdio.h>

double recip( int denom )
{
  return 1.0/(double) denom;
}

void do_nothing(){} /* to  clear the register */

void fcomp( int denom)
{
  double r1, r2;
  int t1, t2;

  r1 = recip(denom); /* stored in memory */
  r2 = recip(denom); /* stored in register */
  t1 = r1 == r2;     /* Compares register to memory */
  do_nothing();      /* Forces register save to memory */
  t2 = r1 == r2;     /* Compares memory to memory */
  printf("test1 t1: r1 %f %c= r2 %f\n", r1, t1 ? '=' : '!', r2);
  printf("test1 t1: r2 %f %c= r2 %f\n", r1, t2 ? '=' : '!', r2);
}

main(){
  int demon = 10;
  fcomp(demon);
}

「O2」オプションを使用した gcc と比較すると、本で提案されている結果は次のようになります。

test1 t1: r1 0.100000 != r2 0.100000
test2 t1: r1 0.100000 == r2 0.100000

しかし、「==」が 2 つ表示され、その理由がわかりません。本の環境設定について何か提案はありますか? どうもありがとう。

4

2 に答える 2

2

この本の例は、(おそらく) Intel CPU の x87 FPU の特定のプロパティを対象としています。この FPU タイプの主なプロパティは、(目に見える) 80 ビット精度のレジスタのみを提供することです。したがって、32 ビットまたは 64 ビットの浮動小数点数は、FPU レジスタにロードされるときに 80 ビットの浮動小数点数に変換されます。さらに、通常、算術演算は完全な精度で実行されるため、後で使用するために値が FPU レジスタに保持される場合、メモリにコピーされてからロードされる値に対して行われるように、32 ビットまたは 64 ビットに丸められません。後で戻る。このため、値がレジスタに保持されているかどうかで違いが生じます。

ただし、Mac OS X (Macbook で使用していると思われます) は x87 FPU を使用せず、SSE ユニットを使用します。SSE は 32 ビットと 64 ビットの浮動小数点レジスタと操作を提供するため、値がレジスタに保持されるか、その精度に関してメモリに格納されます。結果は、各操作の後、常に丸められます。これは通常、Windows および Linux の 64 ビット実行可能ファイルにも適用されます。

たとえば 32 ビット、Linux、または Windows では状況が異なります。x87 または SSE ユニットの使用は環境によって異なります。多くの場合、32 ビット マシンは必要な SSE2 命令をサポートしない可能性があるため、x87 FPU が使用されますが、SSE2 を使用しない最後の CPU は約 10 年前に構築されました。

于 2013-10-12T11:51:51.950 に答える
0

たいした答えではありませんが、ちょっと調べてみました。この fcomp.c http://csapp.cs.cmu.edu/public/1e/ics/code/data/fcomp.cを見つけました。これはおそらくあなたの本の同じ例からのものですが、あなたのバージョンは最初のテストのみが含まれています。とにかく、さまざまな異なる gcc バージョンと -m32 と -m64 を試してみたところ、少なくとも i386 と x86_64 では、test1 (テストと同じ) が常に等しくなることがわかりました。

ただし、アーキテクチャに依存する動作を示すように見える 1 つのテスト (test2) があります。

void test2(int denom)
{
  double r1;
  int t1;
  r1 = recip(denom);             /* Default: register, Forced store: memory */
  t1 = r1 == 1.0/(double) denom; /* Compares register or memory to register */
  printf("test2 t1: r1 %f %c= 1.0/10.0\n", r1, t1 ? '=' : '!');  
  printf("A long double on this machine requires %d bytes\n", sizeof(long double));
}

(test2() は 10 のデーモンで呼び出されます)

でコンパイルするgcc -m64 -o fcomp fcomp.cと、次の出力が得られます。

test2 t1: r1 0.100000 == 1.0/10.0
A long double on this machine requires 16 bytes

でコンパイルするgcc -m32 -o fcomp fcomp.cと、次の出力が得られます。

test2 t1: r1 0.100000 != 1.0/10.0
A long double on this machine requires 12 bytes

記録として、gcc 3.4.6 と 4.1.2 の両方でこれらの結果を得ました。

使用するコンパイラ/アーキテクチャに関係なく、他のすべてのテストは同じ結果になります。

于 2013-10-11T17:20:16.357 に答える