Аномалия с плавающей точкой, когда неиспользуемое утверждение не закомментировано?

Когда программа, как показано ниже, выполняется, она выдает нормальный результат:

 j=         0    9007199616606190.000000 = x
 k=         0    9007199616606190.000000 = [x]
 r=  31443101                   0.000000 = m*(x-[x]) 

Но когда закомментированная строка (т.е. //if (argc>1) r = atol(argv[1]);) не закомментирована, она выдает:

 j=     20000    9007199616606190.000000 = x
 k=     17285    9007199616606190.000000 = [x]
 r=  31443101                   0.000000 = m*(x-[x]) 

хотя эта строка не должна иметь никакого эффекта, поскольку argc>1 ложно. Есть ли у кого-нибудь правдоподобное объяснение этой проблемы? Воспроизводима ли она на каких-либо других системах?

 #include <stdio.h>
 #include <stdlib.h>
 #include <math.h>
 int main(int argc, char *argv[]) {
   int  j, k, m=10000;
   double r=31443101, jroot=sqrt(83), x;
   //if (argc>1) r = atol(argv[1]);
   x = r * r * jroot;
   j = m*(x-floor(x));
   k = floor(m*(x-floor(x)));
   printf ("j= %9d   %24.6f = x\n", j, x);
   printf ("k= %9d   %24.6f = [x]\n", k, floor(x));
   printf ("r= %9.0f   %24.6f = m*(x-[x]) \n", r, m*(x-floor(x))); 
   return 0;
 }

Примечание, тестовая система = система AMD Athlon 64 5200+ с Linux 2.6.35.14-96.fc14.i686 (т.е. , загруженная для запуска 32-битной ОС на 64-битной HW) с gcc (GCC) 4.5. 1 2010924 (Red Hat 4.5.1-4)

Обновление -- Несколько часов назад я опубликовал комментарий о том, что код, сгенерированный с использованием оператора if и без него, отличается только смещением стека и некоторым пропущенным кодом. Теперь я обнаружил, что этот комментарий был не совсем верен; т.е. он верен для неоптимизированного кода, но не верен для кода -O3, который я выполнил.

Влияние переключателя оптимизации на проблему:

  • -O0 : Обе версии программы работают нормально
  • -O2 или -O3: Версия с комментарием имеет ошибку, как указано выше, где j=20000 и k=17285
  • -O1 : Версия с комментарием имеет j=20000 (ошибка) и k=0 (OK)

Так или иначе, глядя на листинги кода -O3 -S, эти два случая отличаются в основном пропущенным if кодом и смещением стека до строки перед call floor, в этот момент код with-if имеет на один fstpl больше, чем код without-if:

    ...  ;; code without comment:
fmul    %st, %st(1)
fxch    %st(1)
fstpl   (%esp)
fxch    %st(1)
fstpl   48(%esp)
fstpl   32(%esp)
call    floor
movl    $.LC2, (%esp)
fnstcw  86(%esp)
movzwl  86(%esp), %eax
    ...
    ...  ;; versus code with comment:
fmul    %st, %st(1)
fxch    %st(1)
fstpl   (%esp)
fxch    %st(1)
fstpl   48(%esp)
fstpl   32(%esp)
fstpl   64(%esp)
call    floor
movl    $.LC3, (%esp)
fnstcw  102(%esp)
movzwl  102(%esp), %eax
    ...

Я не выяснил причину разницы.

8
задан James Waldby - jwpat7 24 November 2011 в 06:08
поделиться