如何对CGFloat进行四舍五入

23

我编写了这个方法。

+ (CGFloat) round: (CGFloat)f {
    int a = f;
    CGFloat b = a;
    return b;
}

它按预期工作,但只舍去小数部分。如果是负数,它仍然向下舍入。

这只是我制作的一个快速方法,它的正确舍入并不是非常重要,我只是为了舍入我的游戏相机的x和y值而制作了它。

这个方法可行吗?它快吗?还有更好的解决方案吗?

6个回答

71

2018年的答案

这里其他的回答要么已经过时,要么没有给出好的示例。使用Swift内置的rounded函数很容易将CGFloat四舍五入。

let x: CGFloat = 3.5
let y = x.rounded() // 4.0

如果您想原地将值四舍五入,可以使用round

var x: CGFloat = 3.5
x.round() // 4.0

舍入规则

如果您希望更加精确地控制数字的舍入方式,可以使用 FloatingPointRoundingRule

远离零方向

x.rounded(.awayFromZero)

大于零的数字向上舍入,小于零的数字向下舍入。

3.000  ->  3.0
3.001  ->  4.0
3.499  ->  4.0
3.500  ->  4.0
3.999  ->  4.0

-3.000  ->  -3.0
-3.001  ->  -4.0
-3.499  ->  -4.0
-3.500  ->  -4.0
-3.999  ->  -4.0

向下取整

x.rounded(.down)

将具有小数值的任何数字四舍五入为下一个较小的整数。这与 floor(x) 相同。

3.000  ->  3.0
3.001  ->  3.0
3.499  ->  3.0
3.500  ->  3.0
3.999  ->  3.0

-3.000  ->  -3.0
-3.001  ->  -4.0
-3.499  ->  -4.0
-3.500  ->  -4.0
-3.999  ->  -4.0

四舍五入到最接近的偶数或远离零的值

x.rounded(.toNearestOrAwayFromZero)   // same as x.rounded()

十进制数会被四舍五入到最近的整数值。然而,当值正好在中间时(比如3.5-3.5),正数会被向上舍入,负数则向下舍入。

这可能有一个名字很长复杂的称呼,但这通常是人们在学校里学习舍入的方式。如果只使用x.rounded(),则也使用这个规则。

3.000  ->  3.0
3.001  ->  3.0
3.499  ->  3.0
3.500  ->  4.0  ***
3.999  ->  4.0

-3.000  ->  -3.0
-3.001  ->  -3.0
-3.499  ->  -3.0
-3.500  ->  -4.0  ***
-3.999  ->  -4.0

最接近偶数

x.rounded(.toNearestOrEven)

这类似于toNearestOrAwayFromZero,但现在.5的值将四舍五入为最接近的偶数。

3.000  ->  3.0
3.001  ->  3.0
3.499  ->  3.0
3.500  ->  4.0   ***
3.999  ->  4.0
4.500  ->  4.0   ***

-3.000  ->  -3.0
-3.001  ->  -3.0
-3.499  ->  -3.0
-3.500  ->  -4.0   ***
-3.999  ->  -4.0
-4.500  ->  -4.0   ***

朝零方向取整

x.rounded(.towardZero)

这只是截取了任何小数值的效果。如果你需要一个 Int,你可以使用 Int(x) 做同样的事情。

3.000  ->  3.0
3.001  ->  3.0
3.499  ->  3.0
3.500  ->  3.0
3.999  ->  3.0

-3.000  ->  -3.0
-3.001  ->  -3.0
-3.499  ->  -3.0
-3.500  ->  -3.0
-3.999  ->  -3.0

向上取整

x.rounded(.up)

这是 .down 的相反操作。所有的十进制数都会向上取整。这与 ceil(x) 相同。

3.000  ->  3.0
3.001  ->  4.0
3.499  ->  4.0
3.500  ->  4.0
3.999  ->  4.0

-3.000  ->  -3.0
-3.001  ->  -3.0
-3.499  ->  -3.0
-3.500  ->  -3.0
-3.999  ->  -3.0

笔记

  • 不要忘记考虑负数的情况。
  • roundrounded 的结果仍然是 CGFloat。如果需要一个 Int,则需要像这样转换:Int(myCGFloat)
  • 不再需要使用 C 数学函数 round(x)ceil(x)floor(x)。然而,如果你确实使用它们,它们处理 64 位和 32 位体系结构,因此你可能曾经看到的使用 roundfceilffloorf 的答案已经过时了。

SO上一个很好的答案的绝佳例子。谢谢。 - Dakine83
1
这本来是一个很好的答案,但问题标记为 objective-c ... - gog

28

<math.h>中已经有标准函数可以满足您的需求,例如:floorf, ceilf, roundf, rintfnearbyintf (带'f'的代表"float"版本,不带的代表"double"版本)。

最好使用标准方法,不仅因为它们是标准的,而且因为它们在边缘情况下表现更好。

2013年更新(jessedc)

iOS不再只支持32位。有许多其他回答针对这个问题,现在更加相关。

大多数回答都提到了导入tgmath.h


啊,谢谢,假设iPhone是32位的,最好使用那些浮点函数。 :D - Johannes Jensen
2
有人知道一种宏,可以允许编写适用于32位和64位CPU的代码吗? - Clafou
只需创建自己的宏,可以在本页面其他地方查看我的评论。 - Saren Inden

5
一个 CGFloat 被typedef为doublefloat,因此您可以像任何其他实数类型一样对它们进行四舍五入:
CGFloat round(CGFloat aFloat)
{
    return (int)(aFloat + 0.5);
}

注意,虽然该方法适用于小数部分较小的浮点数,但在处理大数值时可能会出现奇怪的结果。

1
你正在重新发明轮子 - 这是一个C语言问题,而不是Objective C。只需使用标准的C round()函数即可。

1
我很好奇为什么这个回答在同一天内收到了两个踩 - 特别是因为它是一个完全正确的答案,只是没有被采纳的那个答案详细。 - Paul Lynch
round() 用于 doubles,而 roundf() 用于 floats。在 32 位系统上,CGFloatfloat,而在 64 位系统上是 double - hfossli
仍然没有解释为什么会有负评 - 记住这是三年前的事情! - Paul Lynch
可能是由于CGFloat被重新定义为double的理论可能性所导致的。 - hfossli

1

尝试使用#import "tgmath.h"

<tgmath.h>头文件将包含<math.h><complex.h>头文件,并定义多个类型通用的宏。


0

如果要处理32位和64位,您可以创建自己的宏,例如:

#ifndef CGFLOAT_CEIL
    #ifdef CGFLOAT_IS_DOUBLE
        #define CGFLOAT_CEIL(value) ceil(value)
    #else
        #define CGFLOAT_CEIL(value) ceilf(value)
    #endif
#endif

PS:这不是对问题的回答,而是关于如何处理32/64位值的问题的补充。

在文件中定义MyMacros.h,然后在myapp-Prefix.pch中添加导入即可。

还要记得选择CGFloat作为函数前缀可能存在风险,因为苹果可能会自己添加这样的宏,所以#ifndef CGFLOAT_CEIL


2
我不会在这里重新发明轮子。正如其他答案中提到的那样,tgmath.h(“类型通用”数学)已经为处理架构不同的类型提供了良好的宏。 - Matt Gibson
这只是一个示例,可以应用于多个解决方案。也许您不同意这个示例,但是您能告诉我如何使用NSUserDefaults来存储CGFloat吗?它有设置float或double的函数。就像NSNumber没有检索CGFloat的功能一样。有很多方法可能会有用。当然,我确实同意您不应该重新发明轮子。 - Saren Inden
tgmath对于大多数人来说是过度设计,他们只需要合适的round/floor/ceil。我也不喜欢它重新定义标准函数,而不是使用新名称。我更喜欢这种简单的宏方法。 - zeroimpl

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