61687

[code]

    double one() {
        volatile double result = 0.0;
        for (int i=0; i<10; ++i) {
            result += 0.1;
        }
        return result;
    }
    
    int main(void)
    {
        double a = one();
        assert(a==1.0);
        return 0;
    }
    
    
[/code]

これが通らない(可能性がある)というのは有名な話ですが、

[code]

    int main(void)
    {
        double a = one();
        double b = a;
        double c = one();
    
        assert(a==a); // 1.同じ変数同士の比較
        assert(a==b); // 2.代入結果との比較
        assert(a==c); // 3.同じ方法での生成結果同士の比較
    
        return 0;
    }
    
[/code]

これらが通ることは言語レベルやコンパイラレベルで保証されているのでしょうか。  
サンプルはCで書いていますが、様々な言語でどうなっているのか知りたいです。特にC,C++,C#,Python,JavaScript,Rustに興味があります。


CC BY-SA 4.0
2019-12-21T02:01:28.323
61631
2019-12-23T14:01:11.097
2019-12-21T02:16:15.673
19110
20885
question
アルゴリズム 浮動小数点数
誤差を含む浮動小数点数を比較することについて


浮動小数点演算はランダムに誤差が乗るわけではありません。`1 + 1`が常に`2`であるのと同じように`0.1 +
0.1`の結果は（同一プロセッサーであれば）常に同じ値となります。その上で、

[code]

    volatile double result = 0.0;
    
[/code]

や

[code]

    result += 0.1;
    
[/code]

のように演算に関わる数はすべて定数であることから、これらの演算の結果は全て一致します。

* * *

なお、なぜ誤差が生じるかというと、`0.1`という定数が正確ではなく、`double`型で表現できる最も近い値とされ、この時点で誤差が生じています。その上で10回加算することでその誤差が10倍に広がっているだけです。


CC BY-SA 4.0
2019-12-21T09:29:26.627
61646
2019-12-21T09:29:26.627
4236
61631
answer


現在普通に手に入るコンピュータでは、組み込み用マイコンからスーパーコンピューターまでのほぼ全てで IEEE754 (ISO/IEC/IEEE 60559)
を採用しているので、よほど古いないしは特殊な機械を除き同じ処理をさせれば同じ結果を得ます。「キャッシュ」は文字通り「隠されている代物」なので、あってもなくても同じ結果が出なければなりません。キャッシュの有無によって結果が異なるのであれば、それは「隠されていない」のでキャッシュと呼んではいけないっす。

IEEE754
で内部表現できる数値は正確ですので、それら同士の演算結果は正確で、つまり必ず同じ結果が出ます。丸めモードが違うと結果が異なることはありますが、それは丸めモードを指定していない（機器固有の初期状態に依存している）プログラムの不備です。

同じ演算を行うプログラムが同じ結果を返さないのであればコンピュータとは呼べません（再現実験できないことになる） [Pentium FDIV
Bug](https://ja.wikipedia.org/wiki/Pentium_FDIV_%E3%83%90%E3%82%B0)
なんかも再現パターンを踏ませればきっちり再現します。

  * 浮動小数点数演算自体にて誤差は発生しない
  * 誤差が発生するのは入出力の際に１０進数表記と２進数表記を変換するとき

* * *

コンピュータにとって与えられた値（の下位ビット）に意味があるかないかはどうでもよくて、コンピュータは単に演算するだけです。で、どんな演算であれ（バグがない限り）同じ入力に対しては必ず同じ結果が得られます。下位ビットに意味があるか無いかを決めるのは人間。「誤差」なるものを意味付けするのも人間です。逆に言えば、その辺の事情を理解せずに「誤差」を論じても無意味っす。

１０進数表記文字列を浮動小数点数にする、あるいはその逆を行う関数が、その内部実装が違うことによって精度が違う可能性はあります。なので入力から出力まで二進数で行い精度落ちを排除したなら以下略。
CPU の気分次第で結果が違うなんてことはありません。

* * *

丸めモードを変更する関数を自分で呼び出すということは、丸めモードを意識的に変更したわけで、結果は違って当然（違うように変えたのだから）。今どきの OS
では、この丸めモードはプロセスやスレッドごとに保持できるようになっているので、他プロセスやスレッドが変更しても自分の丸めモードが勝手に変わることはありません。


CC BY-SA 4.0
2019-12-23T00:55:14.133
61687
2019-12-23T11:18:13.203
2019-12-23T11:18:13.203
8589
8589
61631
answer


技術的な興味は理解できますが、実務で浮動小数点数の等値式を使う気にはなりません。できることなら避けるか、幅を持たせて比較します。  
仮に、今の時点で「言語レベルやコンパイラレベルで保証されている」としても、将来にわたって保証されるかは疑問であると考えるからです。  
他言語に移植することや他のコンパイルに変わることを考えると、その仕様をあてにするは危険だと思います。

整数ですら等値式を使うことがためらわれます。

[code]

    for(int i = 0; (i+1) == N; i++)
    
[/code]

と書かかず、

[code]

    for(int i = 0; i < N; i++)
    
[/code]

と書く理由は単に簡潔さだけではないと考えます。


CC BY-SA 4.0
2019-12-23T14:01:11.097
61712
2019-12-23T14:01:11.097
35558
61631
answer