1

以下のこれらのケースについて明確にしたいだけです:

#define MAP_CELL_SIZE_MIN 0.1f

float mMapHeight = 256;
float mScrHeight = 320;

int mNumRowMax;

ケース 1:

mNumRowMax = mMapHeight/( MAP_CELL_SIZE_MIN * mScrHeight );

mNumRowMaxは現在 7 ですが、実際には 8 (256/32) である必要があり、の定義MAP_CELL_SIZE_MINを onlyに変更すると0.1true になり、mNumRowMax8 になります。'f'

ケース 2:

float tmp = mMapHeight/( MAP_CELL_SIZE_MIN * mScrHeight );//tmp = 8.0
mNumRowMax = tmp;

mNumRowMaxmNumRowMaxが 8 になったので、 7の場合の最初のケースの何が問題なのかを理解するのを手伝ってくれる人はいますか?

4

3 に答える 3

2

何が起こるかは

5 [式]

10 浮動オペランドの値および浮動式の結果の値は、型で必要とされるよりも高い精度と範囲で表すことができます。それによって型が変更されることはありません。55)

55)キャストおよび代入演算子は、5.4、5.2.9、および 5.17 で説明されているように、特定の変換を引き続き実行する必要があります。

(C++03; C99 の 6.3.1.8(2) と C11 の n1570 ドラフトと実質的に同一; C++11 の要点は同一であると確信しています。)

以下では、IEEE-754 のような 2 進浮動小数点表現を想定しています。

分数の 16 進数表記では、

1/10 = 1/2 * 3/15
     = 1/2 * 0.33333333333...
     = 2^(-4) * 1.999999999...

bしたがって、それが精度のビットに丸められると、

2^(-4) * 1.99...9a   // if b ≡ 0 (mod 4) or b ≡ 1 (mod 4)
2^(-4) * 1.99...98   // if b ≡ 2 (mod 4) or b ≡ 3 (mod 4)

ここで、小数部分の最後の 16 進数は、それぞれ 3、4、1、2 の最上位ビットの後に切り捨てられます。

ここで、ビットに丸められたwhere320 = 2^6*(2^2 + 1)の結果は、完全な精度で (2 の累乗を無視して)、r * 320r0.1b

   6.66...68
 + 1.99...9a
 -----------
   8.00...02

orおよびb+3のビット付きb ≡ 0 (mod 4)b ≡ 1 (mod 4)

   6.66...60
 + 1.99...98
 -----------
   7.ff...f8

またはb+2のビット付き。b ≡ 2 (mod 4)b ≡ 3 (mod 4)

いずれの場合も、結果を精度のビットに丸めると正確に 32 になり、最終結果としてb得られます。256/32 = 8しかし、精度の高い中間結果を使用すると、

256/(0.1 * 320)

8より少し小さいか大きい。

24 (23+1) ビットの精度を持つ典型的な 32 ビットfloatでは、中間結果が少なくとも 53 ビットの精度で表される場合:

0.1f = 1.99999ap-4
0.1f * 320 = 32*(1 + 2^(-26))
256/(0.1f * 320) = 8/(1 + 2^(-26)) = 8 * (1 - 2^(-26) + 2^(-52) - ...)

intケース1では、結果は中間結果から直接変換されます¹。中間結果は 8 よりわずかに小さいため、7 に切り捨てられます。

floatケース 2 では、中間結果はに変換される前に a に格納されるintため、最初に 24 ビットの精度に丸められ、正確に 8 になります。

ここで、fサフィックスを省略した場合0.1double(おそらく 53 ビットの精度で)、2 つの s が計算のためにfloat昇格され、double

0.1 = 1.999999999999ap-4
0.1 * 320 = 32*(1 + 2^(-55))
256/(0.1 * 320) = 8 * (1 - 2^(-55) + 2^(-110) - ...)

計算がdouble精度1 + 2^(-55) == 1で実行され、すでに0.1 * 320 == 32.

計算が 64 ビットの精度 (x87 と考えてください) 以上の拡張精度で実行される場合、リテラル0.1は精度にまったく変換されずdouble、拡張精度で直接使用される可能性が高く、これも乗算の0.1 * 320結果となります。ちょうど32。

リテラル0.1が精度で使用されdouble、計算がより高い精度で実行される場合、中間結果がより高い精度の表現から に直接切り捨てられた場合は 7 になりint、余分な精度が に変換される前に削除された場合は 8 になりintます。

(余談: gcc/g++ 4.5.1 は、最適化レベルに関係なく、すべてのケースで 8 を生成します。私の 64 ビット ボックスでは、32 ビット ボックスでは試していません。)

¹完全にはわかりませんが、それは標準に違反していると思います。最初に余分な精度を削除する必要があります。言語弁護士はいますか?

于 2012-05-11T12:56:19.960 に答える
0

浮動小数点数が整数にキャストされると、値は丸められずに切り捨てられます。つまり、すべての小数は単に切り捨てられます。

于 2012-04-18T01:59:38.673 に答える
0

丸め誤差が発生しているようです。

簡単な修正は、float の代わりに double を使用することです。

それができない場合は、整数に丸める必要があるかもしれません。たとえば、浮動小数点値 f がある場合は、次のようにします。int x = (int)(f + 0.5);

于 2012-04-18T02:00:52.357 に答える