10

いくつかの C コードと、置き換えようとしている F# を比較すると、最終結果にいくつかの違いがあることがわかりました。

コードを調べてみると、小さな違いではありますが違いがあることがわかりました。

コードは、ファイルからデータを読み取ることから始まります。そして最初の数字は異なって出てきます。たとえば、F# の場合 (スクリプトが簡単):

let a = 71.9497985840
printfn "%.20f" a

期待される (私にとっての) output が得られます71.94979858400000000000

しかし、Cでは:

a =  71.9497985840;
fprintf (stderr, "%.20f\n", a);

プリントアウトし71.94979858400000700000ます。

その7はどこから来たのですか?

違いはほんのわずかですが、理由がわからないので気になります。(コードの 2 つのバージョンが分岐している場所を追跡するのが難しくなるため、私も気になります)

4

5 に答える 5

3

違いは .NET の System.Double.ToString() メソッドであり、double を文字列に変換するメソッドです。SSCLI20 で提供されている CLR ソースをダウンロードすると、関連するコードを確認できます。変換は、clr/src/vm/comnumber.cpp、COMNumber::FormatDouble() 関数によって行われます。これは次のようになります。コード内のコメントは、何が起こっているかを最も説明しています。

//In order to give numbers that are both friendly to display and round-trippable,
//we parse the number using 15 digits and then determine if it round trips to the same
//value.  If it does, we convert that NUMBER to a string, otherwise we reparse using 17 digits
//and display that.

C ランタイム ライブラリにはその機能がありません。

于 2012-06-05T21:16:02.373 に答える
0

これにつまずいた人のための詳細情報。

hereにあるコードのビットを使用して、基になるバイナリ表現 (少なくともこの特定の数値) が同じであるという主張を確認しました (私は信じています)。

コード サンプルを次に示します。負のゼロを削除するために「ゼロをゼロで乗算」していることに注意してください。これは long に変換すると見苦しくなります。

//(C# this time)
var d = 71.9497985840;   //or other incoming double value
if(d == 0) d = d * d;    //for negative zero
var longval = System.BitConverter.DoubleToInt64Bits(d); // = 4634763433907061836

C:

double d;
long long a;
d = 71.9497985840;      //or other incoming double value
if(d == 0) d = d * d;   //for negative zero
a = *(long long*)&d;    //= 4634763433907061836

更新- 各システムが異なるライブラリを呼び出し、異なる方法で反転を実装したため、行列の反転中に不一致が発生していることを発見しました...

于 2012-06-06T11:46:33.147 に答える
0

他の回答は、問題の原因を適切に説明しています (倍精度と丸め)。

数値が一般的に中程度の大きさで、10 進数の精度が (計算速度よりも) 非常に重要である場合は、おそらく .NETdecimal形式の使用を検討してください。これにより、double のような小数点以下の 2 進数の丸め誤差なしで、28 ~ 29 桁の正確な小数点以下の精度が得られます。制限は、範囲が小さいことです (大きな指数はありません!)。

http://msdn.microsoft.com/en-us/library/364x0z75%28v=vs.100%29.aspx

于 2012-06-05T12:29:05.923 に答える