4

重複の可能性:
浮動小数点比較

C/C++ の float の精度に問題があります。以下のプログラムを実行すると:

#include <stdio.h>

int main (void) {
    float a = 101.1;
    double b = 101.1;
    printf ("a: %f\n", a);
    printf ("b: %lf\n", b);
    return 0;
}

結果:

a: 101.099998
b: 101.100000

float には 32 ビットが必要なので、101.1 を格納するには十分なはずです なぜ?

4

6 に答える 6

15

IEEE754 で数値を正確に表すことができるのは (少なくとも単精度および倍精度のバイナリ形式の場合)、使用可能なビット数に応じて、2の逆べき乗 (つまり、、など) を加算して構成できる場合のみです。精度のために。2-n11/21/41/65536

float (23 ビットの精度)またはdouble (52 ビットの精度)によって提供されるスケーリング内で、正確に 101.1 になる 2 のべき乗の反転の組み合わせはありません。

この 2 のべき乗の反転がどのように機能するかについての簡単なチュートリアルが必要な場合は、この回答を参照してください。

その答えからの知識をあなたの数値に適用します101.1(単精度浮動小数点数として):

s eeeeeeee mmmmmmmmmmmmmmmmmmmmmmm    1/n
0 10000101 10010100011001100110011
           |  | |   ||  ||  ||  |+- 8388608
           |  | |   ||  ||  ||  +-- 4194304
           |  | |   ||  ||  |+-----  524288
           |  | |   ||  ||  +------  262144
           |  | |   ||  |+---------   32768
           |  | |   ||  +----------   16384
           |  | |   |+-------------    2048
           |  | |   +--------------    1024
           |  | +------------------      64
           |  +--------------------      16
           +-----------------------       2

その仮数部分は実際には永遠に続きます101.1

mmmmmmmmm mmmm mmmm mmmm mm
100101000 1100 1100 1100 11|00 1100 (and so on).

したがって、精度の問題ではなく、IEEE754 形式でその数値を正確に表す有限ビットの量はありません。

ビットを使用して実際の数 (最も近い近似値) を計算すると、符号は正になります。指数は 128+4+1 = 133 - 127 バイアス = 6 なので、乗数は 2 6または 64 です。

仮数部は、1 (暗黙の基数) と (n は 1 から始まり、右に増加するため、それぞれが 1/(2 n ) の値を持つすべてのビットに対して) で構成され{1/2, 1/16, 1/64, 1/1024, 1/2048, 1/16384, 1/32768, 1/262144, 1/524288, 1/4194304, 1/8388608}ます。

これらをすべて足し合わせると、 になります1.57968747615814208984375

これに、前に計算した乗数 を掛けると64、 が得られ101.09999847412109375ます。

すべての数値はbc100 桁のスケールを使用して計算されたため、多くの後続ゼロが発生するため、数値非常に正確である必要があります。結果を次のように確認したため、二重にそうです。

#include <stdio.h>
int main (void) {
    float f = 101.1f;
    printf ("%.50f\n", f);
    return 0;
}

それ私に与えました101.09999847412109375000...

于 2012-09-28T07:38:41.213 に答える
4

浮動小数点数がどのように機能するか、特に表現可能な数の部分についてもっと読む必要があります。

「101.1には32ビットで十分だ」と思う理由についてはあまり説明していないので、反論するのは難しいです。

2進数の浮動小数点数は、基本的に2を基数として数値を格納するため、すべての10進数に対して適切に機能するわけではありません。2進数の場合と同様です。

これはよく知られている事実であり、たとえば、お金を浮動小数点で処理してはならない理由です。

于 2012-09-28T07:33:53.603 に答える
4

baseのあなたの番号は101.1baseにあります。その部分は繰り返されています。したがって、何桁の数字を持っていても、その数値をコンピューターで正確に表現することはできません。101100101.0(0011)20011

浮動小数点のIEE754標準を見ると、doubleバージョンが完全に表示されているように見える理由がわかります。

PS: in base の派生はin 101.1baseです:101100101.0(0011)2

101 = 64 + 32 + 4 + 1
101 -> 1100101

.1 * 2 =  .2 -> 0
.2 * 2 =  .4 -> 0
.4 * 2 =  .8 -> 0
.8 * 2 = 1.6 -> 1
.6 * 2 = 1.2 -> 1
.2 * 2 =  .4 -> 0
.4 * 2 =  .8 -> 0
.8 * 2 = 1.6 -> 1
.6 * 2 = 1.2 -> 1
.2 * 2 =  .4 -> 0
.4 * 2 =  .8 -> 0
.8 * 2 = 1.6 -> 1
.6 * 2 = 1.2 -> 1
.2 * 2 =  .4 -> 0
.4 * 2 =  .8 -> 0
.8 * 2 = 1.6 -> 1
.6 * 2 = 1.2 -> 1
.2 * 2 =  .4 -> 0
.4 * 2 =  .8 -> 0
.8 * 2 = 1.6 -> 1
.6 * 2 = 1.2 -> 1
.2 * 2....

1/3PPS: in baseの結果を正確に保存したい場合も同じです10

于 2012-09-28T07:39:15.697 に答える
2

印刷物の桁数が多い場合は、正確に表すことさえできないことがdoubleわかります。double

 printf ("b: %.16f\n", b);

 b: 101.0999999999999943

問題は、バイナリ形式を使用していることでfloatあり、doubleすべてのフローティングポインタ番号をバイナリ形式で正確に表すことができるわけではありません。

于 2012-09-28T07:34:01.047 に答える
2

ここに表示されるのは、次の 2 つの要因の組み合わせです。

  • IEEE754 浮動小数点表現は、有理数とすべての無理数のクラス全体を正確に表すことができません。
  • での丸めの効果 (デフォルトでは、ここでは小数点以下 6 桁まで) printf。つまり、使用時のエラーdoubleは 6 番目の DP の右側のどこかで発生します。
于 2012-09-28T07:40:04.913 に答える
1

残念ながら、ほとんどの 10 進浮動小数点数は (マシン) 浮動小数点数では正確に表すことができません。これが物事の仕組みです。

たとえば、101.1 という数字は 2 進数で1100101.0(0011)( の0011部分が永遠に繰り返される ) のように表されるため、何バイト格納しても正確にはなりません。これは、浮動小数点のバイナリ表現に関する小さな記事です。ここでは、浮動小数点数をバイナリに変換する例をいくつか見つけることができます

このテーマについて詳しく知りたい場合は、この記事をお勧めしますが、長くて読みにくいです。

于 2012-09-28T07:37:24.400 に答える