我之前主要使用整数,在需要将 float 或 double 截断为整数的情况下,我之前会使用以下内容:
(int) someValue
除非我发现以下内容:
NSLog(@"%i", (int) ((1.2 - 1) * 10)); // prints 1
NSLog(@"%i", (int) ((1.2f - 1) * 10)); // prints 2
(请参阅 Strange behavior when casting a float to int in C# 的解释)。
简短的问题是:我们应该如何截断一个 float 或 double 来正确地转换为整数? (在这种情况下需要截断,而不是“舍入”)。或者,我们可以说,由于一个数字是 1.9999999999999,另一个是 2.00000000000001(粗略地说),截断实际上是正确完成的。所以问题是,我们应该如何转换 float 或 double,以便结果是一个具有常见用法意义的“截断”数字?
(目的是不使用 round
,因为在这种情况下,对于 1.8
,我们确实想要 1
的结果,而不是2
)
较长的问题:
我用过
int truncateToInteger(double a) {
return (int) (a + 0.000000000001);
}
-(void) someTest {
NSLog(@"%i", truncateToInteger((1.2 - 1) * 10));
NSLog(@"%i", truncateToInteger((1.2f - 1) * 10));
}
并且都打印为 2
,但这似乎太过分了,我们应该使用多少小数字来“消除不准确”?有没有更标准或经过研究的方法,而不是这种任意的 hack?
(注意在某些用法中我们要截断,而不是四舍五入,例如,如果秒数是 90 或 118,当我们显示经过了多少分钟和多少秒时,分钟应该显示为 1
,但不应四舍五入为 2
)
最佳答案
当然,截断已正确执行,但中间值不准确。
通常没有办法知道您的 1.999999
结果是否稍微不准确 2
(因此截断后的精确数学结果是 2
),或者稍微不准确的 1.999998
(因此截断后的精确数学结果为 1
)。
就此而言,对于某些计算,您可能会得到 2.000001
作为稍微不准确的 1.999998
。几乎无论你做什么,你都会弄错那个。截断是一个非连续函数,因此无论您如何操作,它都会使您的整体计算在数值上不稳定。
无论如何您都可以添加任意公差:(int)(x > 0 ? x + epsilon : x - epsilon)
。它可能有帮助,也可能没有帮助,这取决于你在做什么,这就是为什么它是一个“黑客”。 epsilon
可以是常数,也可以根据 x
的大小进行缩放。
第二个问题的最常见解决方案不是“消除不准确”,而是接受不准确的结果,就好像它是准确的一样。因此,如果您的浮点单元表示 (1.2-1)*10
是 1.999999,那么,它是 1.999999。如果该值表示分钟数,则它会截断为 1 分 59 秒。您最终显示的结果将与真实值相差 1 秒。如果您需要比这更准确的最终显示结果,那么您不应该使用浮点运算来计算它,或者您应该在截断为分钟之前四舍五入到最接近的秒。
任何尝试从 float 中“消除”不准确性实际上只会改变不准确性 - 一些输入会给出更准确的结果,而另一些则不太准确。如果您足够幸运,可以将不准确性转移到您不关心的输入中,或者可以在进行计算之前过滤掉,那么您就赢了。但总的来说,如果你必须接受任何输入,那么你就会在某个地方迷路。您需要研究如何使您的计算更准确,而不是试图在最后的截断步骤中消除不准确。
您的示例计算有一个简单的更正 - 使用以 10 为底的小数位的定点算术。我们知道 format 可以准确表示 1.2。因此,您不应编写 (1.2 - 1) * 10
,而应重新调整计算以使用十分之一(编写 (12 - 10) * 10
),然后除以最终结果结果乘以 10 以将其缩放回单位。
关于objective-c - 在 C 和 Objective-C 中,将 float 或 double 截断为整数的真正正确方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11244713/