簡體   English   中英

正確減去浮點值

[英]Properly subtracting float values

我正在嘗試創建一個值數組。 這些值應為“ 2.4,1.6,.8,0”。 我在每一步都減去0.8。

這就是我的做法(代碼段):

float mean = [[_scalesDictionary objectForKey:@"M1"] floatValue];  //3.2f
float sD = [[_scalesDictionary objectForKey:@"SD1"] floatValue];   //0.8f

nextRegion = mean;
hitWall = NO;
NSMutableArray *minusRegion = [NSMutableArray array];


while (!hitWall) {

    nextRegion -= sD;

if(nextRegion<0.0f){
    nextRegion = 0.0f;
    hitWall = YES;
}

[minusRegion addObject:[NSNumber numberWithFloat:nextRegion]];

}

我得到以下輸出:

minusRegion = (
    "2.4",
    "1.6",
    "0.8000001",
    "1.192093e-07",
    0
)

我不想在0.8和0之間的數字小得令人難以置信。是否有標准方法來截斷這些值?

3.2和.8都不能完全表示為32位浮點數。 最接近3.2的可表示數字是3.2000000476837158203125(以十六進制浮點數表示,0x1.99999ap + 1)。 最接近.8的可表示數字是0.800000011920928955078125(0x1.99999ap-1)。

從3.2000000476837158203125中減去0.800000011920928955078125后,精確的數學結果為2.400000035762786865234375(0x1.3333338p + 1)。 此結果也不能完全表示為32位浮點數。 (您很容易在十六進制浮點數中看到這一點。32位浮點數的有效位數為24位。“ 1.3333338”在“ 1”中具有一位,在中間六位為24位,而在“ 8”。)因此,將結果四舍五入到最接近的32位浮點數,即2.400000095367431640625(0x1.333334p + 1)。

從中減去0.800000011920928955078125得到1.6000001430511474609375(0x1.99999cp + 0),這是可以精確表示的。 (“ 1”是一位,五個九是20位,“ c”有兩個有效位。“ c”中的低兩位是尾隨零,可以忽略。所以有23個有效位)

從中減去0.800000011920928955078125得出0.800000131130218505859375(0x1.99999ep-1),這也可以精確表示。

最后,從中減去0.800000011920928955078125得到1.1920928955078125e-07(0x1p-23)。

這里要學習的教訓是,浮點數並不代表所有數字,它會將結果四舍五入,以便為您提供可以代表的最接近的數字。 在編寫使用浮點算術的軟件時,您必須了解並允許這些舍入運算。 一種允許這樣做的方法是使用您知道的可以表示的數字。 其他人建議使用整數算法。 另一個選擇是使用大多數您知道可以精確地以浮點表示的值,其中包括不超過2 24的整數。 因此,您可以從32開始並減去8,得出24,然后是16,然后是8,然后是0。這些將是您用於循環控制和繼續計算而沒有錯誤的中間值。 當您准備好交付結果時,可以除以10,得出3.2、2.4、1.6,.8和0(准確)附近的數字。 這樣,您的算術運算法則只會在每個結果中引入一個舍入誤差,而不是在每次迭代之間累積舍入誤差。

這樣做的另一種方法是將減法得到的數字乘以10,然后轉換為整數,然后將該整數除以10.0。

您可以使用下限功能(floorf)輕松完成此操作,如下所示:

float newValue = floorf(oldVlaue * 10)/ 10;

您正在查看良好的舊浮點舍入錯誤。 幸運的是,您的情況應該很容易處理。 只需夾緊:

if( val < increment ){
    val = 0.0;
}

不過,正如Eric Postpischil所說明的那樣

用這種方式進行鉗位不是一個好主意,因為有時舍入會導致迭代變量比增量略小而不是增量,並且這種鉗位將有效地跳過迭代。 例如,如果初始值為3.6f(而不是3.2f),步長為.9f(而不是.8f),則每次迭代中的值都將略低於3.6、2.7、1.8和.9。 到那時,鉗位會將值略低於.9的值轉換為零,並且跳過迭代。

因此,進行比較時可能需要減去少量。

您應該考慮的一個更好的選擇是使用整數而不是浮點數進行計算,然后再進行轉換。

int increment = 8;
int val = 32;

while( val > 0 ){
    val -= increment;

    float new_float_val = val / 10.0;
};

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM