iOS Objective-C 如何获得保留两位小数的浮点数?

29

我有一个

float a = 1412.244019;

我需要将a四舍五入到最接近的第二位小数,例如1412.24000000。我知道如果我想要只显示两位小数,可以使用%.2f,但这并不是我的情况。出于数学原因,我需要新的浮点数a。

我尝试了一些在stackoverflow上找到的方法,但都没有成功。我使用的比较明显的一个方法如下,但仍然无法使用。你能看出问题出在哪里吗?

PS: 它有时会产生期望的效果,但并非总是,这让我深思...

提前感谢您。

代码:

float a = 1412.244019;
NSLog(@"a is %f", a); //output a is 1412.244019
a = [[NSString stringWithFormat:@"%.2f", a] floatValue];
NSLog(@"a is %f", a); //output a is 1412.239990

编辑:

看起来,在我使用上述手术后,当我使用浮点数a时,它认为它是1412.240000,尽管NSLog说得不同...奇怪。但我得到了我想要的结果,所以感谢浪费时间追逐无用之物 :)

编辑:我很想选择你们所有人作为正确答案,但由于我只能选择一个,我选择了最好的答案之一,并附加了解释(最后两个答案中的一个)。

6个回答

60

你尝试过这个吗?

CGFloat val = 37.777779;

CGFloat rounded_down = floorf(val * 100) / 100;   /* Result: 37.77 */
CGFloat nearest = floorf(val * 100 + 0.5) / 100;  /* Result: 37.78 */
CGFloat rounded_up = ceilf(val * 100) / 100;      /* Result: 37.78 */

来源: C语言中保留两位小数

补充说明:你无法控制计算机如何存储浮点数值。

因此这些舍入后的值可能不是完全精确的“明显”小数值,但它们会非常接近,这是您能得到的最大保证。


就像我在编辑中所说的那样,浮点数无法获得完全精度。 - Lucas Eduardo
我明白。它正在按照我的意愿计算。但我认为这很奇怪,因为那样你将永远无法使用精确的算术? - B-Man
是的,精度通常足够进行计算,并且误差足够小以被忽略。 - Lucas Eduardo
好的。谢谢你的回答和时间 :) 我会采用我得到的结果。 - B-Man
谢谢指出,那个例子并不好展示我的意思。已经编辑过了。 - Lucas Eduardo
显示剩余2条评论

11

在 Objective-C 中,您可以像在 C 语言中一样执行该操作:

double notRounded = ...;
double rounded = round (notRounded * 100.0) / 100.0;

除非您能向别人准确解释为什么要使用float而不是double,否则请不要使用float。float的精度非常有限。使用ceil或floor也是愚蠢的,因为有一个完全可以胜任的标准函数名为“round”。

无论是float还是double都不能准确地表示大多数十进制值,比如12.24。由于双精度更高,使用double将使您更接近12.24。因此,当使用(float) 12.24时,NSLog可能会显示一些奇怪的数字,比如12.23999997394等,而对于double,则可能会显示12.24。


1
谢谢你的回答。Double 是正确的选择。我之前已经选了一个答案,所以我只能点赞你的回答。感谢你花时间解释,非常感激。 - B-Man

6

这一行出现了错误

a = [[NSString stringWithFormat:@"%.2f", a] floatValue];

将其更正为

a = [NSString stringWithFormat:@"%.02f", a];

5
离1412.24最接近的float值是1412.239990234375。没有任何操作可以生成比这更接近的float,因为float对象的格式不表示任何更接近的值。要求用float表示1412.24就像要求用int表示2.5一样,根本无法完成。
处理此问题的选项包括:
- 您可以使用格式化显示“1412.24”,而不更改底层的float对象。 - 您可以使用double来获得更接近(更接近),但仍然不会完全等于1412.24。 - 您可以将float值缩放到可表示的值(例如,以其他单位测量,使您想要的值在float中完全可表示)。 - 您可以使用其他数据类型,如NSDecimal。

我感谢你的回答,我想我必须找到另一种方法。但请参见上面的编辑。尽管NSLog给出了“错误”的格式,但它确实正确使用了它。我将看看是否使用你上面提到的方法能够得到更好的结果。 - B-Man
1
@B-Man,C语言的float类型无法保证完美舍入。如果您需要这样做,请使用另一种数据类型。您可以自己编写代码将值保留在100分之一的NSInteger值中,或者使用上面列出的其他数据类型,例如NSDecimal - bshirley

2
您可以将a乘以100.0,再加上0.5进行四舍五入,然后取结果的int值。然后使用/ 100获取小数点前的值,使用% 100获取两位小数。

我尝试了那个但没成功。请看上面的编辑(两分钟后到达)。 - B-Man

2

你不能强制使用浮点数。浮点数被设计成近似值,因为它们的数量级可能很大,而且由于只有那么多比特可用,所以可以接近但不完全表示十进制数。

一个建议是在整数中进行所有算术运算,乘以100,然后在显示最终结果时除以100。你可能有太大的数字会阻止这样做。


我感谢你的回答,我想我必须另寻他路。但请参见上面的编辑。尽管 NSLog 给出了“错误”的格式,但它仍然正确地使用了它。 - B-Man
NSLog告诉你的是真相...但你可能正在使用其他格式/算术运算,这些运算会平衡(因为浮点算术是设计成这样的)以使你接近正确答案。但你会发现有时它会按照你的期望进行计算,有时会出现不应该存在的尾数,或者数字会变短。当你打印答案时,或者尝试比较两个浮点数时(例如2.0!= 5.0-3.0),这种情况最为明显。如果你需要精确到小数点后两位的精确算术,你应该像上面那样使用整数和/100。 - Owen Hartnett

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接