-1

floatを小数点以下 6 桁に丸めようとしています。double から float への変換はほとんどこれを行うようですが、その過程で奇妙なことに気付きました。私が何をしても、後でdoubleに変換すると、最後に架空の余分な 10 進数の値になってしまうようです。double への変換を避けることはできません。これは変更できないアプリケーションの一部であるため、コードが元々存在しなかった余分な小数点以下の桁数を生成している理由を理解しようとしています。

例えば; 次の値から始めて、double に変換します。

float foo = 50.8467178;
double bar = Convert.ToDouble32(foo); 
// Sorry line above originally said ToInt32 which was a typo :(

..その後、 「バー」は 、デバッガーでは50.846717834472656 になります。

ただし:

Convert.ToDouble(50.8467178).ToString()

...生成: 50.8467178 (デバッガー内)。つまり、余分な値はありません。

最後に余分な数字が表示されるのはなぜですか? どうすればこれを止めることができますか? そして最後に: 上記のいずれかで ToString() を呼び出すと、デバッガーが表示するものとは異なる小数点以下の桁数が出力されることが多いのはなぜですか?

4

5 に答える 5

0

この例では、実際には微妙に異なることがいくつかあります。

float を小数点以下 6 桁に丸めようとしています。double から float への変換は、ほとんどこれを行うようです

丸める必要がある場合は、Roundメソッドを使用します。変換すると精度が失われる可能性があり、浮動小数点型はそもそも無限に正確ではありません。

最後に余分な数字が表示されるのはなぜですか?

内部的に float と double は、 IEEE 754標準に従ってバイナリで表されます。あなたの数字 50.8467178 は、1/3 を 10 進数で (数字を繰り返さずに) 正確に表すことができないのと同じように、2 進数で正確に表すことはできません。標準では、float としての数値は として格納され0x424B6304ます。これは、24 ビットまたは 6 ~ 9 桁の 10 進数まで正確です。ここで、標準に従ってその数値を再解釈すると、次のようになります。

0x424B6304
= 1.58895993232727 * 2^5
= 50.84671783447264

デバッガーであろうとToString()メソッドの呼び出しであろうと、ディスプレイは、最初の 6 ~ 9 桁のみが重要であることを認識できるほどスマートです (これは、IEEE 標準と一致しています)。最大 9 桁を取ると、float は のように表示され50.8467178ます。

ただし、この数値を double に変換しても、バイナリ値は変わりません。その内部ビット パターンは確かにそうですが、2 進数は依然として として解釈され1.58895993232727 * 2^5ます。問題は、double が 53 ビット、つまり 10 進数で 15 ~ 17 桁まで正確であることです。そのため、表示されると、元の 6 ~ 9 桁ではなく、15 ~ 17 桁が表示されます。したがって、倍精度に変換されたときに余分な数字があるわけではなく、数字はすでにそこにありました。

中間の float 変換がない場合、double を使用して 50.8467178 をより正確に表すことができます。として保存され0x40496C613FB5F875ます。これについてはまだ計算していませんが、上記の手順を使用すると、次のような結果が得られます50.84671780000000000000000000023155。最初の 15 ~ 17 桁のみを考慮すると、ディスプレイに表示されるのは 50.8467178 になります (有効な 0 は省略されています)。

どうすればこれを止めることができますか?

float にキャストして丸めないでください。正確な数字は 6 ~ 9 桁しかありません。通常、10 進数型は 10 進数の精度が必要な場合に適していますが、コードのその部分を変更することはできないため、このRoundメソッドを使用することはアプリケーションにとって十分な精度である必要があります (1,000,000,000 未満である限り)。

上記のいずれかで ToString() を呼び出すと、デバッガーが表示するものとは異なる小数点以下の桁数が出力されることが多いのはなぜですか?

私はこの問題を回避して、物事を単純かつ連続的に保つように努めてきました。精度の範囲を 10 進数で指定しています。浮動小数点数の場合は 6 ~ 9、倍精度浮動小数点数の場合は 15 ~ 17 です。double を例にとると、このToString()メソッドはデフォルトで 10 進数 15 桁の文字列を返します。ToString("G17") ただし、 (doc)を呼び出して 17 桁の 10 進数を返すように強制することはできますが、これらの 2 桁が有効であるという保証はありません。デバッガーが表示のためにこのバージョンを呼び出していると思われます。これが と異なる理由ToString()です。


詳細な資料: IEEE 算術、Oracle による。かなりテクニカルですが。

于 2013-06-01T06:52:10.520 に答える
-1

リテラル 50.8467178 はデフォルトで double であるため、foo に割り当てられると暗黙的に float に変換されます。また、フロートは精度が小さいため、丸みを帯びており、それほど正確ではありません。float リテラルであるため、foo に代入するときは 50.8467178f を使用する必要があります。2 番目の例では、double リテラルを double に変換しているため、変更はなく、そのまま出力されます。

于 2013-05-30T12:03:30.423 に答える
-1

これを行う:

Convert.ToDouble(50.8467178).ToString()

これを行うのと同じです:

(50.8467178).ToString();

数値の暗黙の型は であるためdouble、変換は何もしません。

しかし、数値を として開始するとfloat、変換が有効になり、double何度も混乱することが知られています (この場合、値を比較する場合など)。

の代わりにdecimal, ダイレクト キャスト andを使用することをお勧めします。Equals==

于 2013-05-30T12:03:33.000 に答える
-1

余分な小数点以下の桁数が表示される理由は、double から float に変換する際のわずかな精度の低下が原因である可能性があります (浮動小数点の変換は完全ではありません)。

数値を丸める必要がある場合は、変換を行った後に次のことができます。

result = Math.Round(result, 2);

編集:以下のエリックのコメントを考えると、ここにいくつかの追加情報があります:

C#の Afloatは数値の 32 ビット浮動小数点表現ですが、adoubleは 64 ビットです。すべての数値が浮動小数点数で完全に表現できるわけではありません。32 ビット float から 64 ビット double に変換すると、情報を入力するために 32 ビットが余分に必要になります。この追加情報は、表示される追加の数字になります。

float foo = 50.8467178f;
double bar = System.Convert.ToDouble(foo);
System.Console.WriteLine(bar); // prints 50.8467178344727 on my machine

元の数値は浮動小数点で完全に表現できるわけではないため、変換プロセスにより、出力はわずかに異なる数値になります。

この数値の精度を維持する必要がある場合は、decimal代わりに値を使用する必要があります。

decimal foo = 50.8467178M;
double bar = System.Convert.ToDouble(foo);
System.Console.WriteLine(bar); // prints 50.8467178 on my machine

使用できない場合decimal(たとえば、floatサードパーティのライブラリなどの出力である場合)、丸めアプローチが次善の策です。

float foo = 50.8467178f;
double bar = System.Convert.ToDouble(foo);
bar = System.Math.Round(bar, 7);
System.Console.WriteLine(bar); // prints 50.8467178 on my machine
于 2013-05-30T12:04:25.187 に答える