5

これが私が書いたコードです:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    double num;
    int tmp;
    printf("enter a number!\n");
    scanf("%lf",&num);
    tmp=num*10000;
    printf(" temp=%d\n",tmp);

    return 0; 
}

数値1441.1441を入力すると、14411441ではなく14411440になります。これは、入力数値に10000を掛けた後の正しい結果です。誰かがこの問題を理解するのを手伝ってくれますか?

4

4 に答える 4

11

1441.1441実数の大部分は実際には正確に表すことができないため、実際にはのようなものとして格納されていることに気付くでしょう1441.14409999_blah_blah_blah。あなたは挿入することによってそれを見つけることができます:

printf ("%.50lf\n", num);

との直後scanf(末尾のゼロは削除されます):

1441.14409999999998035491444170475006103515625

これで、入力に基づく正しい(つまり最も近い)値になります。そこから次に大きい数字はあなたに与えます:

1441.144100000000207728589884936809539794921875

最初の値のエラーは次のとおりです。

0.00000000000001964508555829524993896484375
               ^ ~ 2 x 10^-14

2番目のエラーは次のとおりです。

0.000000000000207728589884936809539794921875
              ^ ~ 2 x 10^-13

後者のエラーは約10倍であることがわかります。

これに乗算し10000て靴べらにしようとすると、int切り捨てられます(切り捨てられます)。これは、(C11)標準が次のように言っているためです6.3.1.4

実際の浮動小数点型の有限値が_Bool以外の整数型に変換されると、小数部は破棄されます(つまり、値はゼロに向かって切り捨てられます)。

試すことができることの1つは、シューホーニングラインを次のように変更することです。

tmp = num * 10000 + 0.5;

これにより、切り捨てが効果的に丸め演算に変わります。これはすべての場合に機能すると思いますが、念のためにテストする(そして監視する)ことをお勧めします。

于 2012-11-23T08:38:57.210 に答える
1

一般原則として、paxdiabloの回答には関連する部分が含まれています。ほとんどの終了小数は、2進浮動小数点数として正確に表すことができないため、浮動小数点変数の値は、指定された文字列の数値表現の数学値よりも少し小さいか大きいため、適切な整数を取得する場合スケーリング後の値は、切り捨てではなく丸める必要があります。

しかし、ここでの特定の例では、別のシナリオがあります。1441.1441に最も近いIEEE754倍精度(64ビットバイナリ)値は次のとおりです。

1441.14409999999998035491444170475006103515625

これは確かに1441.1441より少し小さいです。ただし、その値にIEEE754倍精度値として10000を掛けると、結果は正確になります。

14411441

ここで何が起こるかというと、5.2.4.2.2パラグラフ9で許可されているように

代入とキャスト(余分な範囲と精度をすべて削除する)を除いて、浮動オペランドと通常の算術変換の対象となる値を持つ演算子と浮動定数によって生成される値は、範囲と精度がタイプ

(強調鉱山)、製品はタイプ(おそらくx87 80ビット形式)で必要とされるよりも高い精度で評価され、わずかに小さい値が生成され、乗算の結果がに変換されるintと、小数部分は破棄されます、および14411440を取得します。

scanf("%lf",&num);

値はに格納されるnumため、精度は正確に。である必要がありdoubleます。

tmp=num*10000;

製品num * 10000は保存もキャストもされないdoubleため、精度が高くなり、最も近い値よりも小さい値または大きい値になる可能性がありdoubleます。次に、その値が切り捨てられて。が取得されintます。

double製品を変数に保存した場合

num *= 10000;
tmp = num;

doubleまたは、に変換する前にキャストしますint

tmp = (double)(num * 10000);

入力の結果14411441を取得する必要があります1441.1441(ただし、すべてのコンパイラが、キャストまたは保存時に必要な正確な精度に変換する要件を常に満たすわけではないことに注意してください-標準に違反しています-したがって、すべての最適化設定で14411441が生成される保証はありません)。

多くの64ビットプラットフォームは、x87コプロセッサーではなくSSE命令を使用して浮動小数点演算を実行するため、観察された動作は、32ビットシステムよりも64ビットシステムで発生する可能性が低くなります。

于 2012-11-23T13:13:31.873 に答える
0

そのように丸くしてみてください:

float a = 3.14;

int i = (int)(a+0.5);

あなたの場合:

 double num;
 int tmp;
 printf("enter a number!\n");
 scanf("%lf",&num);
 tmp=(int)(num*10000 + 0.5);
 printf(" temp=%d\n",tmp);
于 2012-11-23T08:43:03.040 に答える
-4

scanfはscanf内でfloat精度を使用しているようです。1441.1441がfloatで1441.1440として表されていることを簡単に確認しました。一般的に、浮動小数点演算の精度に依存するべきではありません。

于 2012-11-23T08:56:06.013 に答える