4

次のコード行があります。

hero->onBeingHit(ENEMY_ATTACK_POINT * (1.0 - hero->getDefensePercent()));
  • void onBeingHit(int decHP)メソッドは整数を受け取り、健康ポイントを更新します。
  • float getDefensePercent()method は、ヒーローの防御率を返す getter メソッドです。
  • ENEMY_ATTACK_POINTとして定義されるマクロ定数係数#define ENEMY_ATTACK_POINT 20です。

hero->getDefensePercent()returnとしましょう0.1。なので計算は

20 * (1.0 - 0.1)  =  20 * (0.9)  =  18

次のコードで試したときはいつでも(f追加なし1.0

hero->onBeingHit(ENEMY_ATTACK_POINT * (1.0 - hero->getDefensePercent()));

17になりました。

しかし、次のコードの場合 (f後に追加1.0)

hero->onBeingHit(ENEMY_ATTACK_POINT * (1.0f - hero->getDefensePercent()));

私は18になりました。

どうしたの?すでにフロートにfなっていますが、まったく重要ですか?hero->getDefensePercent()

4

3 に答える 3

11

どうしたの?18どちらの場合も整数の結果にならないのはなぜですか?

問題は、浮動小数点式の結果が整数値に変換されるときにゼロに向かって丸められることです (どちらの場合も)。

0.1浮動小数点値として正確に表すことはできません (両方の場合)。コンパイラは、バイナリ IEEE754 浮動小数点数への変換を行い、表現可能な値に切り上げるか切り下げるかを決定します。次に、プロセッサは実行時にこの値を乗算し、結果を丸めて整数値を取得します。

わかりましたが、 と の両方がそのようdoublefloat動作するのに、なぜ182 つのケースのいずれかになるのに17、もう一方のケースになるのでしょうか? よくわかりません。

コードは関数の結果0.1f(float) を取得20 * (1.0 - 0.1f)し、どちらが double 式で、20 * (1.0f - 0.1f)は float 式であるかを計算します。float バージョンはたまたま よりわずかに大きく、18.0に切り捨てられますが18、 double 式は よりわずかに小さく18.0、 に切り捨てられ17ます。

IEEE754 2 進浮動小数点数が 10 進数からどのように構成されているか正確にわからない場合、コードに入力した 10 進数よりわずかに小さいかわずかに大きい場合は、ほとんどランダムです。したがって、これを当てにしてはいけません。f数値の 1 つに追加して、「今は機能するので、そのままにしておきます」と言って、このような問題を修正しようとしないでくださいf。別の値が再び異なる動作をするためです。

式の型が this の優先度に依存するのはなぜfですか?

これは、C および C++ の浮動小数点リテラルがdoubleデフォルトの型であるためです。を追加するfと、フロートになります。浮動小数点式の結果は「より大きな」タイプです。double 式と整数の結果は依然として double 式であり、int と float は float になります。したがって、式の結果は float または double のいずれかになります。

わかりましたが、ゼロに丸めたくありません。一番近い数字に丸めたい。

この問題を解決するには、結果を整数に変換する前に、結果に半分を追加します。

hero->onBeingHit(ENEMY_ATTACK_POINT * (1.0 - hero->getDefensePercent()) + 0.5);

C++11 にはstd::round()、そのための機能があります。標準の以前のバージョンでは、最も近い整数に丸める関数はありませんでした。(詳しくはコメントをご覧ください。)

持っていない場合はstd::round、自分で書くことができます。負の数を扱うときは注意してください。整数に変換する場合、数値は切り捨てられます (ゼロに向かって丸められます)。つまり、負の値は切り捨てではなく切り上げられます。したがって、数値が負の場合は半分を引く必要があります。

int round(double x) {
    return (x < 0.0) ? (x - .5) : (x + .5);
}
于 2013-02-15T08:00:17.937 に答える
4

これが発生する理由は、を使用したときのより正確な結果doubleです1.0

結果を丸めるようにしてください。これにより、変換後の積分結果がより正確になります。

hero->onBeingHit(ENEMY_ATTACK_POINT * (1.0 - hero->getDefensePercent()) + 0.5);

その直後に追加0.5して切り捨てると結果が丸められることに注意してください。int17.999...18.499...18

于 2013-02-15T08:00:03.980 に答える
4

1.0 は double として解釈されますが、コンパイラによって float として認識される 1.0f とは対照的です。

f サフィックスは、どちらが float でどちらが double であるかをコンパイラに伝えるだけです。

名前が示すように、double は float の 2 倍の精度を持ちます。一般に、double の精度は 15 ~ 16 桁ですが、float の精度は 7 桁です。

この精度の低下により、切り捨てエラーが発生しやすくなる可能性があります

MSDN (C++) を参照してください

于 2013-02-15T07:55:38.590 に答える