2017-12-16 11 views
1

このプログラムは、ユーザーが提供する精度でpiを計算する必要があります。 calculate_pi()関数はNASMで記述されています。asm関数を呼び出す前にCでprintfを呼び出すかどうかの不思議な副作用はありますか?

//printf("accuracy: %.15f\n", precision);  //<- This line 

プログラムが正しく動作しません。この行がコメント化されている場合は、なぜ は、誰かが私に説明できます。 calcuta_pi()関数に奇妙な数字を送りますか?この行がコメントされている場合、関数には非常に小さな値が送られ、プログラムは無限に実行されます。

しかし、コメントされていないプログラムが正しく動作するかどうか。

#include <stdio.h> 
#include <math.h> 

extern double calculate_pi(double precision); /* external function declaration */ 

double calculate_pi(double precision); /* function prototype */ 

int main() 
{ 
double precision = 1; 

printf("A program that calculates pi, with accuracy provided by the user\n"); 
printf("Give me accuracy\n"); 

while(1) 
{ 
    if (scanf("%lf", &precision) != 1) 
    { 
     printf("reading error\n"); 

     fseek(stdin,0,SEEK_END); 

     continue; 
    } 
    if(precision<0) 
     precision = fabs(precision); 

    //printf("accuracy: %.15f\n", precision);  //<- This line 
    printf("pi: %.15f\n", calculate_pi(precision)); 
} 

return 0; 
} 

これは私のアセンブリコードです:私が使用している

enter image description here

  • NASMバージョン2.11.06を上にコンパイル

    ; arctg(1)=a 
    ; tg(arctg(1))=tg(a) 
    ; atan(x) = x - x^3/3 + x^5/5 - x^7/7 + x^9/9.. 
    ; PI/4 = atan(1) = 1 - 1/3 + 1/5 - 1/7 + 1/9... 
    ; PI = (4/1) - (4/3) + (4/5) - (4/7) + (4/9) - (4/11) + (4/13) - (4/15) ... 
    
    
    section .text use32 
    
    global _calculate_pi 
    
    _calculate_pi: 
    
    %idefine a [ebp+12] 
    
    
    ;ramka stosu 
    push ebp 
    mov ebp, esp 
    
    ;ustawianie zmiennych 
    fld qword [const_wynik] 
    fstp qword [wynik] 
    
    fld qword [const_licznik] 
    fstp qword [licznik] 
    
    fld qword [const_mianownik] 
    fstp qword [mianownik] 
    
    .loop: 
    finit ; inicjalizacja stosu FPU 
    
    fld qword [licznik]   ;licznik na stos 
    fld qword [mianownik]  ;mianownik na stos 
    fdiv      ;wynik dzielenia st1/st0 
    fadd qword [wynik]   ;st0 = wynik dzielenia + [wynik] 
    
    fstp qword [wynik]   ;wywalamy z st0 do [wynik] 
    
              ;zmieniamy mianownik + 2 
    fld qword [mianownik]  ;mianownik na stos 
    fadd qword [zwiekszmian] ;st0 = mianownik + 2 
    fstp qword [mianownik]   ;wywalamy z st0 do [mianownik] 
    
              ;zmieniamy licznik *(-1) 
    fld qword [licznik]   ;licznik na stos 
    fchs      ;st0 = -st0 = -licznik 
    fstp qword [licznik]  ;wywalamy z st0 do [licznik] 
    
             ;sprawdzanie dokladnosci 
    fld qword[wynik]  ;wynik na stos 
    fldpi     ;pi na stos 
    fsub     ;st0 = wynik-pi = st1 - st0 
    fabs     ;st0 = |wynik-pi| 
    
    fld qword a    ;st0 = zadana dokladnosc 
    
              ;(Unordered Compare ST(0) to ST(i) and set CPU flags and Pop ST(0)) 
              ;Przyrostek p oznacza obniżenie stosu rejestrów koprocesora, przyrostek i oznacza zapisywanie wyników bezpośrednio do flag procesora a nie flag koprocesora 
    fucomip st0, st1   ;porownanie z dokladnoscia if(zadana dokladnosc > uzyskana) 
    
    
    jb .loop ;only the C0 bit (CF flag) would be set if no error 
    
    
    fld qword [wynik] 
    
         ;zwraca to co w st0 
    leave ; LEAVE = mov esp, ebp/pop ebp 
    ret 
    
    
    section .data: 
    
    wynik  dq 4.0 
    licznik  dq -4.0 
    mianownik dq 3.0 
    zwiekszmian dq 2.0 
    
    const_wynik  dq 4.0 
    const_licznik dq -4.0 
    const_mianownik dq 3.0 
    

    サンプル出力2014年10月20日

  • のgcc(GCC MinGW.org-6.3.0-1)6.3.0

コンパイルとアセンブラのコマンド:

​​
+0

これは非常にユーザーフレンドリーな精度の指定方法ではありません。正しい桁数が予想されます。 – molbdnilo

+0

どのように正しく動作しないか説明してください。 – molbdnilo

+0

次の場合、プログラムは正しく動作しません。 calculate_pi()関数は、arctg(1)のテイラー式でpiを計算します。 この行がコメントすると、関数には非常に小さな値が送信され、プログラムは無限に実行されます。 – LeoProXXX

答えて

3

私はあなたがでオフセット、誤っ機能の引数にアクセスしていると思います4バイト。スタックフレームを作成すると、最初の引数は[ebp+8]になりますが、[ebp+12]からロードしています。 (これがスタックに引数を渡すすべての呼び出し規則に適用されます。私は、32ビットのmingwのは、デフォルトでdoubleのためのことを行い思います。)

これは、あなたがprecisionとして使用しているdouble値が何であれからその高い4つのバイトを有することを意味します呼び出し側は8バイトのargスロットの上にスタックを置いていました。これは、呼び出し元の変更が関数の動作に影響を与える理由と無限ループを得る理由を説明しています。ロードするバイトが非常に小さいdoubleを表すようになった場合、ループは終了しません。

下位4ビット(仮数の最下位32ビット)は、呼び出し側が渡したものの上位4バイトから来ます。

レジスタを調べて、デバッガでこれを簡単に見つけて、ロードした値が呼び出し元が渡した値ではないことに気づくでしょう。また、@ Ped7gの提案では、ちょっとしたasm関数でprecisionを返そうとすると、問題が見つかったでしょう。

+0

ありがとうございました。それは問題を解決しました。 – LeoProXXX

関連する問題