2009-04-15 4 views
12

この明示的なキャストの結果が暗黙的なキャストの結果と異なるのはなぜですか?この明示的なキャストの結果が暗黙のキャストの結果と異なるのはなぜですか?

#include <stdio.h> 

double a; 
double b; 
double c; 

long d; 

double e; 

int main() { 
    a = 1.0; 
    b = 2.0; 
    c = .1; 

    d = (b - a + c)/c; 
    printf("%li\n", d);  // 10 

    e = (b - a + c)/c; 
    d = (long) e; 
    printf("%li\n", d);  // 11 
    } 

私がd =(long)((b-a + c)/ c)を行うと、私はまた10を得る。二重への割り当てはなぜ違いを生むのだろうか?

+0

私のシステムでは同じ(両方とも11)ですか? –

+0

これは何をコンパイルしていますか? – Joseph

+0

ちょっと面白くて、eをローカル変数にして、それが変わるかどうかを調べてみてください。 –

答えて

16

違いは、80ビット浮動小数点値から80ビット浮動小数点値への変換と64ビット値への変換で、からに変換することが長いと思われます。

(80ビットが全く来た理由は、それが実際の演算に使用される典型的な精度であるということであり、浮動小数点レジスタの幅。)

は、80ビットの結果が10.999999999999999ようなものであると仮定 - しかし、80ビットの値に最も近い64ビットの浮動小数点値は実際には11.0なので、2段階の変換は11になります。

EDIT:これを与えるには、次のようにします。より多くの重量を...

ここでは、任意精度の算術演算を使用してsamを実行するJavaプログラムですe計算。これは0.1に最も近いdouble値をBigDecimalに変換することに注意してください。その値は0.1000000000000000055511151231257827021181583404541015625です。 (換言すれば、計算の正確な結果がとにかくない 11である)

import java.math.*; 

public class Test 
{ 
    public static void main(String[] args) 
    { 
     BigDecimal c = new BigDecimal(0.1d);   
     BigDecimal a = new BigDecimal(1d); 
     BigDecimal b = new BigDecimal(2d); 

     BigDecimal result = b.subtract(a) 
          .add(c) 
          .divide(c, 40, RoundingMode.FLOOR); 
     System.out.println(result); 
    } 
} 

ここで結果は次のとおりすなわち

10.9999999999999994448884876874217606030632 

は、その途中(約40桁に正しいです64ビットまたは80ビット浮動小数点以上が処理可能)。

ここでは、この番号がバイナリでどのように見えるかを考えてみましょう。私は簡単に変換を行うためのツールはありませんが、再びJavaを使用して手助けすることができます。正規化された数を仮定すると、 "10"部分は3ビットを使用して終了する(1つは1111 = 1011未満である)。これは、拡張精度(80ビット)と倍精度(64ビット)のための48ビットのために60ビットの仮数を残す。

したがって、精度ごとに11に最も近い数字は何ですか?ここでも、のは、Javaを使ってみましょう:

import java.math.*; 

public class Test 
{ 
    public static void main(String[] args) 
    { 
     BigDecimal half = new BigDecimal("0.5");   
     BigDecimal eleven = new BigDecimal(11); 

     System.out.println(eleven.subtract(half.pow(60))); 
     System.out.println(eleven.subtract(half.pow(48)));   
    } 
} 

結果:

10.999999999999999999132638262011596452794037759304046630859375 
10.999999999999996447286321199499070644378662109375 

そこで、我々が持っている3つの数字は、以下のとおりです。

Correct value: 10.999999999999999444888487687421760603063... 
11-2^(-60): 10.999999999999999999132638262011596452794037759304046630859375 
11-2^(-48): 10.999999999999996447286321199499070644378662109375 

は今、正しいものに最も近い値をうまく精度ごとに、精度は11以下です。これらの値のそれぞれをlongにすると、それぞれ10と11になります。

がうまくいけば、これは懐疑を説得するのに十分な証拠である。)

+0

これは、C#で同様の効果が見られたことは間違いありません。これはプロセッサとコンパイラ依存のbtwになります。私は100%これが起こっていると確信していますか?いいえ、それは非常に可能性の高い説明だと思いますか?絶対に。 「自分のマシンで動作する」IMOよりも便利です。 –

+0

http://babbage.cs.qc.edu/IEEE-754/はこの種の作業に非常に役立ちますが、80ビット計算機ではなく、32ビットと64ビットの計算機しかありません。 –

+0

@Adam:ありがとうございました。確かに有用です。最終的な "小数点"の値が最も近い倍精度で表された* exact *値だった場合には便利です。 –

0

ストレートコピー/貼り付けおよびLinux上でコンパイル私の両方のために11を与えます。 d = (long) ((b - a + c)/c);を追加すると11が得られます。OpenBSDでも同様です。

+0

OSは問題になることはまずありません。コンパイラ+オプション+プロセッサははるかに関連性があります。 –

1

codepad.org(gcc 4.1。2)はあなたの例の結果を逆にしますが、ローカルシステム(gcc 4.3.2)ではどちらの場合も11になります。これは浮動小数点問題であることを私に示唆しています。あるいは、理論的には、整数コンテキストでは(2 - 1 + 0)/ .1と評価される(b - a + c)が10になるように切り捨てることができますが、floatコンテキスト(2.0 - 1.0 + 0.1 )/ .1 = 1.1/.1 = 11それは変だけど。

+0

まず、cの値は0.1ではありません。それはちょうど0.1に最も近い倍です。 –

2

gcc 4.3.2を実行している私の32ビットx86 Linuxシステムでは、10 & 11が得られます。

関連するC/ASMはここにある:

26:foo.c   ****  d = (b - a + c)/c;            
    42       .loc 1 26 0 
    43 0031 DD050000    fldl b 
    43  0000 
    44 0037 DD050000    fldl a 
    44  0000 
    45 003d DEE9     fsubrp %st, %st(1) 
    46 003f DD050000    fldl c 
    46  0000 
    47 0045 DEC1     faddp %st, %st(1) 
    48 0047 DD050000    fldl c 
    48  0000 
    49 004d DEF9     fdivrp %st, %st(1) 
    50 004f D97DFA    fnstcw -6(%ebp) 
    51 0052 0FB745FA    movzwl -6(%ebp), %eax 
    52 0056 B40C     movb $12, %ah 
    53 0058 668945F8    movw %ax, -8(%ebp) 
    54 005c D96DF8    fldcw -8(%ebp) 
    55 005f DB5DF4    fistpl -12(%ebp) 
    56 0062 D96DFA    fldcw -6(%ebp) 
    57 0065 8B45F4    movl -12(%ebp), %eax 
    58 0068 A3000000    movl %eax, d 
    58  00 
    27:foo.c   **** 
    28:foo.c   ****  printf("%li\n", d);             
    59       .loc 1 28 0 
    60 006d A1000000    movl d, %eax 
    60  00 
    61 0072 89442404    movl %eax, 4(%esp) 
    62 0076 C7042400    movl $.LC3, (%esp) 
    62  000000 
    63 007d E8FCFFFF    call printf 
    63  FF 
    29:foo.c   ****  // 10               
    30:foo.c   **** 
    31:foo.c   ****  e = (b - a + c)/c;            
    64       .loc 1 31 0 
    65 0082 DD050000    fldl b 
    65  0000 
    66 0088 DD050000    fldl a 
    66  0000 
    67 008e DEE9     fsubrp %st, %st(1) 
    68 0090 DD050000    fldl c 
    68  0000 
    69 0096 DEC1     faddp %st, %st(1) 
    70 0098 DD050000    fldl c 
    70  0000 
    71 009e DEF9     fdivrp %st, %st(1) 
    72 00a0 DD1D0000    fstpl e 
    72  0000 
    32:foo.c   **** 
    33:foo.c   ****  d = (long) e;              
    73       .loc 1 33 0 
    74 00a6 DD050000    fldl e 
    74  0000 
    75 00ac D97DFA    fnstcw -6(%ebp) 
    76 00af 0FB745FA    movzwl -6(%ebp), %eax 
    77 00b3 B40C     movb $12, %ah 
    78 00b5 668945F8    movw %ax, -8(%ebp) 
    79 00b9 D96DF8    fldcw -8(%ebp) 
    80 00bc DB5DF4    fistpl -12(%ebp) 
    81 00bf D96DFA    fldcw -6(%ebp) 
    82 00c2 8B45F4    movl -12(%ebp), %eax 
    83 00c5 A3000000    movl %eax, d 
    83  00 

答えは、興味のある読者のための課題として残されています。

関連する問題