Objective c 正确减去浮点值
我正在尝试创建一个值数组。这些值应为“2.4,1.6,8,0”。我每一步都在减法.8 我就是这样做的(代码片段):Objective c 正确减去浮点值,objective-c,ios,c,floating-point,Objective C,Ios,C,Floating Point,我正在尝试创建一个值数组。这些值应为“2.4,1.6,8,0”。我每一步都在减法.8 我就是这样做的(代码片段): 我不想要一个介于.8和0之间的非常小的数字。有标准的方法来截断这些值吗?另一种方法是将通过减法得到的数字乘以10,然后转换为整数,然后将该整数除以10.0 使用如下地板功能(floorf),您可以轻松完成此操作: 浮动新值=地板(旧值*10)/10 您看到的是良好的旧式浮点舍入错误。幸运的是,在您的情况下,它应该很容易处理。只需夹紧: if( val < increment
我不想要一个介于.8和0之间的非常小的数字。有标准的方法来截断这些值吗?另一种方法是将通过减法得到的数字乘以10,然后转换为整数,然后将该整数除以10.0 使用如下地板功能(floorf),您可以轻松完成此操作:
浮动新值=地板(旧值*10)/10 您看到的是良好的旧式浮点舍入错误。幸运的是,在您的情况下,它应该很容易处理。只需夹紧:
if( val < increment ){
val = 0.0;
}
3.2和.8都不能精确地表示为32位浮点。最接近3.2的可表示数字为3.2000000476837158203125(十六进制浮点,0x1.9999ap+1)。最接近.8的可表示数字为0.800000011920928955078125(0x1.99999 AP-1) 当从3.2000000476837158203125中减去0.800000011920928955078125时,精确的数学结果为2.400000035762786865234375(0x1.333333 8p+1)。这个结果也不能精确地表示为32位浮点。您可以在十六进制浮点中轻松地看到这一点。32位浮点具有24位有效值。“1×333333”在“1”中有一位,中间六位的24位,8位中的另一位。因此,结果被舍入到最接近的32位浮点,即2.400万95367331640625(0xTrace33333 4p+1)。 从中减去0.800000011920928955078125,得到1.6000001430511474609375(0x1.99999 cp+0),这正是可表示的。(1是一位,“5个9”是20位,“c”有两个有效位。“c”中的低位两位是尾随零,可以忽略不计。因此有23个有效位。) 从中减去0.800000011920928955078125,得到0.800001113021850585959375(0x1.99999 EP-1),这也是可精确表示的 最后,从中减去0.800000011920928955078125得到1.1920928955078125e-07(0x1p-23)
这里要吸取的教训是,浮点并不代表所有数字,它会对结果进行四舍五入,以获得它所能代表的最接近的数字。在编写使用浮点运算的软件时,必须理解并考虑这些舍入操作。考虑到这一点的一种方法是使用您知道可以表示的数字。其他人建议使用整数算术。另一种选择是使用大多数可以用浮点表示的值,其中包括最多224个整数。你可以从32开始,减去8,得到24,16,8,0。这些将是用于循环控制和无错误继续计算的中间值。当您准备好交付结果时,您可以除以10,生成接近3.2、2.4、1.6、8和0的数字(精确)。这样,您的算法只会在每个结果中引入一个舍入误差,而不是一次又一次地累积舍入误差。我的意思是截断平均值和SD,这样它们就不会给我很小的值。我编辑了我的答案,以展示使用floorf函数实现这一点的简单方法。我不明白为什么.8的浮点表示的尾数是1.10011001。。。(重复)而不是精确的1.100100000000。(.8用浮点表示时存储为1.6*2^-1。所选的1.6在较低的有效位中有一些古怪的地方,这导致了问题中出现的错误)我认为发生了一些奇怪的事情。我主要关心的是找到一个切实可行的解决办法,但我也对了解官方原因/解释感兴趣。可能我没有使用正确的数据类型(float)。不,我认为float应该可以。注意:10.0中的“.0”在执行除法时很重要。是的,这是类型提升:
一些int/10
会导致int
,这不会产生小数。除以一个文本float
“首先提升”int,这会给出一个float
结果。以这种方式进行钳制是一个坏主意,因为有时舍入会导致迭代变量略小于增量,而不是略大于增量,这种钳制将有效地跳过迭代。例如,如果初始值为3.6f(而不是3.2f),而步长为.9f(而不是.8f),则每次迭代中的值将略低于3.6、2.7、1.8和.9。此时,钳制将略低于.9的值转换为零,并跳过一次迭代。@Eric:我曾考虑在比较if(val<(increment-buffer))
中添加一个“epsilon”,但考虑到问题的简单性,我认为这是一次过多的操作。我认为整数解更可取。不过,对问题的解释很好。
minusRegion = (
"2.4",
"1.6",
"0.8000001",
"1.192093e-07",
0
)
if( val < increment ){
val = 0.0;
}
int increment = 8;
int val = 32;
while( val > 0 ){
val -= increment;
float new_float_val = val / 10.0;
};