首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >在C和Objective中,将浮点数或双倍截断为整数的真正正确方法是什么?

在C和Objective中,将浮点数或双倍截断为整数的真正正确方法是什么?
EN

Stack Overflow用户
提问于 2012-06-28 12:31:52
回答 7查看 5.3K关注 0票数 9

我以前主要使用整数,在需要将浮点数或双倍截断为整数的情况下,我会在前面使用以下方法:

代码语言:javascript
运行
复制
(int) someValue

除非我发现了以下情况:

代码语言:javascript
运行
复制
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#的解释)。

简短的问题是:应该如何正确地截断浮点数或双倍整数?(在这种情况下需要截断,而不是“舍入”)。或者,我们可以说,由于一个数字是1.9999999999999,另一个数字是2.00000000000001 (粗略地说),截断实际上是正确的。因此,问题是,我们应该如何转换一个浮动或双,以便结果是一个“截断”的数字,这是正常使用的意义?

(我们的意图不是使用round,因为在本例中,对于1.8,我们确实需要1的结果,而不是2)

较长的问题:

我用过

代码语言:javascript
运行
复制
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的形式打印出来,但这似乎是一个太大的黑客,我们应该用什么小数字来“消除不准确”呢?是否有更标准或研究的方式,而不是这样武断的黑客?

(请注意,我们希望截断,而不是在某些用法中舍入,例如,如果秒数为90或118,当我们显示出多少分钟和多少秒时,分钟应显示为1,但不应舍入到2)

EN

回答 7

Stack Overflow用户

发布于 2012-06-28 12:44:29

当然,截断操作是正确的,但是中间值不准确。

通常,无法知道您的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秒。最后显示的结果将比实际值少1s。如果您需要更准确的最终显示结果,那么您不应该使用浮点算法来计算它,或者您应该在截断到分钟之前将其舍入到最近的秒。

任何从浮点数字中“消除”不准确的尝试实际上都只是在移动不准确--一些输入会给出更精确的结果,而另一些则不太准确。如果你足够幸运,在这样的情况下,不准确被转移到你不关心的输入,或者在计算之前可以过滤掉,那么你就赢了。但是,一般来说,如果你必须接受任何输入,那么你就会失去一些东西。您需要研究如何使您的计算更精确,而不是试图在最后的截断步骤中消除不精确。

你的例子计算有一个简单的修正我们知道这种格式可以准确地表示1.2。因此,与编写(1.2 - 1) * 10不同,您应该重新计算,使用十分之分(写(12 - 10) * 10),然后将最终结果除以10,将其缩小为单位。

票数 12
EN

Stack Overflow用户

发布于 2012-06-28 13:58:49

当您修改您的问题时,现在的问题似乎是:给定一些输入x,计算一个值f'(x)。f'(x)是精确数学函数f(x)的计算逼近。您想要计算trunc( f(x) ),即离零最远的整数i,而不是比f(X)更远的整数。因为f'(x)有一些错误,所以trunc( f'(x) )可能不等于trunc( f(x) ),例如f(X)为2时,而f‘(X)为0x1.fffffffffp 0。给定f'(x),你如何计算桁架c(f(X))?

这个问题是不可能解决的。对于所有的f,没有没有解决方案。

由于f‘中的误差,f'(x)可能是0x1.fffffffffp0,因为f(x)是0x1.fffffffffp0,或者f'(x)可能是0x1.fffffffffffp 0,即使f(x)是2时,也不可能知道什么是trunc(f(x))。

一个解决方案是可能的,只给出关于f的详细信息(以及用来用f‘逼近f’的实际操作)。你还没有提供这方面的信息,所以你的问题无法得到回答。

这里有一个假设:假设f(x)的性质是这样的,对于某些除以1的q,它的结果总是非负倍数。例如,q可能是.01 (坐标值的百分之一)或1/60 (表示秒的单位,因为f是分钟的单位)。假设计算f‘时使用的值和运算使得f’中的误差总是小于q/2。

在这种非常有限且假设的情况下,可以通过计算trunc(f'(x)+q/2)来计算trunc(f(x))。证明:设I= trunc(f(x))。假设我> 0。然后i <= f(x) < i+1,所以i <= f(x) <= i+1-q (因为f(x)被q量化)。然后i/2< f'(x) < i+1-q+q/2 (因为f'(x)在f(X)的q/2之内)。然后我< f'(x)+q/2 < i+1,然后是trunc(f'(x)+q/2) = i,所以我们得到了想要的结果。如果i= 0,则-1 < f(x) < 1,则-1+q <= f(x) <= 1-q,so -1+q-q/2 < f'(x) < 1-q+q/2,则-1+q < f'(x)+q/2 < 1,则-1+q(f‘(X)+q/2)=0。

(注:如果q/2在所使用的浮点精度中不能精确表示,或者不能轻易地添加到f'(x)中而没有错误,那么必须在证明、其条件或Q/2的加法中做一些调整。)

如果这种情况不符合您的目的,那么就不能通过提供有关f以及计算f‘的操作和值的详细信息来期望回答expect。

票数 3
EN

Stack Overflow用户

发布于 2012-06-28 12:43:28

“黑客”是正确的做法。浮点数的工作原理很简单,如果您想要更多理智的十进制行为,NSDecimal(Number)可能就是您想要的。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/11244713

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档