お知らせ

  • 利用規約を守って投稿してください。また、よくある質問および投稿の手引きも参照してください。
  • メッセージの投稿にはアカウントが必要です。未登録の方は、ユーザ登録ページからアカウントを作成することができます。

#1 2010-05-22 18:52:26

omame
新しいメンバ
登録日: 2010-05-22

GCCバグ?

Ubunutu10.04-AMD64版上のGCC4.4.3なのですが
以下のコードを走らせると、結果が?になります。

-----------------  test.c -------------------
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>

int main(int argc, char** argv) {
    int     i;
    float   L2, val;
    float   *pV, *pV1;
    int     Ndim = 6000000;
//
    pV  = (float *)memalign( 16, sizeof(float)*Ndim );
    pV1 = (float *)memalign( 16, sizeof(float)*Ndim );
//
    for( i=0; i<Ndim; i++ ) {
        pV[i]  = 6.0f;
        pV1[i] = 4.0f;
    }

    L2 = 0.0f;
    for( i=0; i<Ndim; i++ ) {
       L2 += pV[i] * pV1[i];
    }

    printf( "loop count : %d\n", i );
    printf( "sum = %f,  (the expected value : %f)\n", L2, 24.0 * 6000000.0 );

    return (EXIT_SUCCESS);
}
----------------- ここまで ----------------------------------------------------
$ gcc --version
gcc (Ubuntu 4.4.3-4ubuntu5) 4.4.3

$ gcc -m64 test.c -o test

$ ./test
loop count : 6000000
sum = 147260736.000000,  (the expected value : 144000000.000000)

マシンは自作PCで、Q9450定格,RAM8GB,Mobo ASUS-P5Q-PROです。
上記コードのfor{}ループにprintf()を入れて調べたところ
ループカウント  pV[i]*pV1[i]の値  L2の値                差
5592404      :  24.000000            134217720.000000  24.000000  
5592405      :  24.000000            134217744.000000  24.000000 ..... ここまでO.K.
5592406      :  24.000000            134217760.000000  16.000000 ..... ?
5592407      :  24.000000            134217792.000000  32.000000 ..... ??
5592408      :  24.000000            134217824.000000  32.000000 ..... ???
以降
5999998      :  24.000000            147260704.000000   32.000000
5999999      :  24.000000            147260736.000000   32.000000 ... \(T_T)/

ubuntu10.04付属のmemtest86は1回しか調べてませんが、パスしてます。
ループ回数は何回、コンパイル&リンク、実行を繰り替えしても同じループカウント5592406から狂い始めます。
上のコード、何か変でしょうか?

オフライン

 

#2 2010-05-22 21:39:43

vbk
メンバ
From: Lake Hamana in Hamamatsu
登録日: 2009-12-22

Re: GCCバグ?

float(単精度浮動小数点数)型の変数がオーバーフローしたために、意図しない値になっていると思われます。

例えば、浮動小数点数で134217744を表すと、以下のようになります。仮数部は2進数で表しています。
1.00000000000000000000001 × 2^27
これに24を足した、134217768は以下のようになります
1.000000000000000000000101 × 2^27

単精度浮動小数点数型の仮数部は最大23ビットなので、134217768(以降)ではこの制限を超えてしまいます。
float型ではなく、double(倍精度浮動小数点数)型を使うことで解決します。

オフライン

 

#3 2010-05-23 02:20:54

hir0
メンバ
登録日: 2008-09-28

Re: GCCバグ?

オーバーフロー回避の後も気を緩めずに、演算に誤差(狂い)が生じる事を意識して置かないと忘れたころに「アレ?」ってなるよ。

オフライン

 

#4 2010-05-23 06:32:41

omame
新しいメンバ
登録日: 2010-05-22

Re: GCCバグ?

同じ内積を以下のinline_asm文で計算すると正しい答えが帰ってきます。

------------------ test.c -------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>

//
// inner product  L^2 = <V1,V2>
//
void vector_ip( float *L2, float *pV1, float *pV2, int len )
{               //     rdi          rsi          rdx      rcx
    __asm__ __volatile__ (
        "pxor    %%xmm1, %%xmm1        \n"
        "movq    %%rcx,  %%rax              \n"
        "shrq    $2, %%rax                       \n" // rax = len / 4
        "jz      L_1%=                              \n"
"L_0%=:\n"
        "movaps  (%%rsi), %%xmm0        \n"
        "mulps   (%%rdx), %%xmm0         \n"
        "addps   %%xmm0,  %%xmm1      \n"
        "addq    $16, %%rsi                      \n"
        "addq    $16, %%rdx                     \n"
        "subq    $1,  %%rax                      \n"
        "jnz     L_0%=                             \n"
"L_1%=:\n"
        "andb    $0b11, %%cl                    \n" // rcx = mod( len / 4 )
        "jz      L_3%=                              \n"
"L_2%=:\n"
        "movss   (%%rsi), %%xmm0         \n"
        "mulss   (%%rdx), %%xmm0         \n"
        "addss    %%xmm0, %%xmm1       \n"
        "addq    $4, %%rsi                        \n"
        "addq    $4, %%rdx                       \n"
        "subb    $1, %%cl                         \n"
        "jnz     L_2%=                             \n"
"L_3%=:\n"
        "haddps  %%xmm1, %%xmm1      \n"
        "haddps  %%xmm1, %%xmm1      \n"
        "movss   %%xmm1, (%%rdi)        \n"
        :
        :
        : "rdi", "rsi", "rcx", "rdx", "rax", "xmm0", "xmm1"
    );
}


int main(int argc, char** argv) {
    int     i;
    float   L2, val;
    float   *pV, *pV1;
    int     Ndim = 6000000;
//
    pV  = (float *)memalign( 16, sizeof(float)*Ndim );
    pV1 = (float *)memalign( 16, sizeof(float)*Ndim );
//
    for( i=0; i<Ndim; i++ ) {
        pV[i]  = 6.0f;
        pV1[i] = 4.0f;
    }

//  L2 = 0.0;
//  for( i=0; i<Ndim; i++ ) {
//     L2 += pV[i] * pV1[i];
//  }

    vector_ip( &L2, pV, pV1, Ndim );

    printf( "loop count : %d\n", i );
    printf( "sum = %f,  (the expected value : %f)\n", L2, 24.0 * 6000000.0 );

    return (EXIT_SUCCESS);
}
-------------------------- ここまで ------------------------------------------------------

$ gcc -m64 test.c -o test
$ ./test
loop count : 6000000
sum = 144000000.000000,  (the expected value : 144000000.000000)

xmmレジスタを使った単精度計算をasm文で行っているので、内部的に拡張倍精度(80ビット)を
使うfpu計算より精度が落ちるのかと思いましたが、意に反して正しい答えが求まっています。

GCCが1演算(asmレベルで)毎に単精度でメモリへ吐き出す(80ビットー>32ビット)したとしても
xmmレジスタは32ビットため同じ結果になると思うのですが。
xmmレジスタにもガードビットがついていて、実際には32ビット以上で計算されていたと言うことですかね?
vbkさん、hir0さん回答ありがとうございました。

オフライン

 

#5 2010-05-23 07:02:13

omame
新しいメンバ
登録日: 2010-05-22

Re: GCCバグ?

上の件で、「実際には32ビット以上で計算されていた」は
IEEE 754 形式の単精度(符号部 1 ビット ・ 指数部 8 ビット ・ 仮数部 23 ビット)に於いて
仮数部のビット数が23ビット+ガードビットの意味です。

実際には、上記inline_asm文の作成が先行しており、答え合わせのつもりで、後で C で計算を行いました。

xmmレジスタの単精度計算は、メモリ上でのIEEE 754 形式の単精度表現と同じである旨の記載があった為
桁落ちが C 側で発生していることに気がつきませんでした。

オフライン

 

#6 2010-07-31 15:53:19

ua6ta123
メンバ
登録日: 2010-04-02

Re: GCCバグ?

初心者のトンチンカンな質問です。

諸兄の御投稿を興味深く思い、不心得とは存じますが、i686環境で上掲の2つのプログラムをコンパイルし、実行を試みました。
はじめのプログラムの方は、
$gcc test.c -o test
$./test
で実行したところ、omameさんと全く同じ結果が得られました。

次のinline-asm付きのプログラムは、Synapticマネージャで、libc6-dev-amd64をインストールしたところ、
$gcc -m64 test.c -o test
で、エラーや警告なく、testが生成されましたが、
$./test
bash:./test:バイナリファイルを実行できません。
と出ました。(当然と私も思います...)
ちなみに、
$exec ./test    では、
bash:フルパス/test:バイナリファイルを実行できません。
bash:フルパス/test:Success
と出ます。
(初心者で、2行目は何がSuccessなのかも分かりません。)

ここで質問なのですが、libc6-dev-amd64環境では、gccで64ビットのコンパイルが出来ると考えて良いのでしょうか?
そして、もし64ビットのバイナリが正常に生成されるのであれば、実行をエミュレートできるようなソフト(デバッガーなどでも)や方法は存在するのでしょうか?

ちなみに、私のCPUは、
Intel(R) Celeron(R) M CPU 430  @ 1.73GHz
cache size    : 1024 KB
cpuid level    : 10
wp        : yes
clflush size    : 64
cache_alignment    : 64
address sizes    : 32 bits physical, 32 bits virtual

で、Ubuntu10.04-LTS-amd64のインストールも試みましたが、x86-64でなくi686 onlyなのでダメとインストール初期画面ではじかれました。

お答えいただけましたらありがたく思います。宜しくお願い致します。

オフライン

 

#7 2011-01-01 16:31:29

ua6ta123
メンバ
登録日: 2010-04-02

Re: GCCバグ?

謹賀新年

前回の質問で、32ビットマシンで、64ビットのアーキテクチャを模擬できるかどうかというのは、本当に的外れな質問のようでした。

運良く、64ビットマシンを購入できましたので、早速、Ubuntu10.04LTS-amd64をインストールし、
本日、32ビットマシンで、上掲のインラインアセンブラ付きのプログラムを、libc6-dev-amd64により、gccに-m64を付けてコンパイルしたものを、
64ビットマシン上で、実行できることを確認いたしました。

お騒がせいたしました。
今後とも宜しくお願い申し上げます。

2010年1月1日 ua6ta123

オフライン

 

Board footer

Powered by FluxBB