如何在Swift中将Double四舍五入为最接近的整数?

206

我正在尝试制作一个增长率 (Double) 计算器,它将把结果四舍五入到最近的整数并从那里重新计算,如下所示:

let firstUsers = 10.0
let growth = 0.1
var users = firstUsers
var week = 0


while users < 14 {
    println("week \(week) has \(users) users")
    users += users * growth
    week += 1
}

但是到目前为止,我还没有成功。

编辑 我大致是这样做的:

var firstUsers = 10.0
let growth = 0.1
var users:Int = Int(firstUsers)
var week = 0


while users <= 14 {
    println("week \(week) has \(users) users")
    firstUsers += firstUsers * growth
    users = Int(firstUsers)
    week += 1
}

虽然我不介意它总是向下取整,但我不喜欢它,因为firstUsers必须成为一个变量,并在整个程序中改变(为了进行下一次计算),这不是我想要的。

9个回答

296

Foundation 库中有一个 round 函数可用 (实际上它在 Darwin 中,但是 Foundation 导入了 Darwin,大多数情况下你将想要使用 Foundation 而不是直接使用 Darwin)

import Foundation

users = round(users)

在一个代码播放器中运行您的代码,然后调用:

print(round(users))

输出:

15.0

round() 在小数位数大于等于0.5时向上舍入,在小数位数小于0.5时向下舍入(标准舍入)。你可以使用 floor() 强制向下舍入,使用 ceil() 强制向上舍入。

如果你需要四舍五入到特定的位数,则可以将数字乘以 pow(10.0, 位数),执行四舍五入操作,然后再除以 pow(10, 位数)

四舍五入到小数点后两位:

let numberOfPlaces = 2.0
let multiplier = pow(10.0, numberOfPlaces)
let num = 10.12345
let rounded = round(num * multiplier) / multiplier
print(rounded)

输出:

10.12

注意:由于浮点数运算的特性,rounded 可能不总是完全精确。最好将其视为舍入的近似值。如果您是为显示目的而这样做,则最好使用字符串格式化来格式化数字,而不是使用数学进行舍入。


哎呀,很遗憾在游乐场中 pow() 不可用。 - MrBr
1
@MrBr,pow()函数定义在Darwin库中,因此您需要首先import Darwin(或import Foundationimport Cocoaimport UIKit,它们都会在内部导入Darwin)。 - Mike S
75
还有一个函数 lround(),它返回一个整数类型。 - Martin R
1
round()”函数在小数点后面的数字 >= 0.5 时向上舍入,< 0.5 时向下舍入(标准四舍五入)。但是有例外,“round(-16.5)”返回的结果是-17而不是-16。这是一个错误吗? - Daniel T.
2
@DanielT. - 不是一个错误。它会四舍五入到最接近的更大的负数。 这样想,+16.5到+17就是离零点再远0.5。这意味着-16.5到-17也比零点远0.5。Ceil则相反,+16.5到+16就是离零点更近0.5,而-16.5到-16也是离零点更近0.5。 - adougies

168

要将double类型的数字四舍五入到最近的整数,请使用round()函数。

var x = 3.7
x.round() // x = 4.0
如果您不想修改原始值,则使用rounded()函数:
let x = 3.7
let y = x.rounded() // y = 4.0. x = 3.7

正如人们所预料(或者未曾预料),像 3.5 这样的数字会被四舍五入为最接近的整数,而像 -3.5 这样的数字将会被向下舍入。如果您需要不同的舍入行为,可以使用其中一个舍入规则。例如:

var x = 3.7
x.round(.towardZero) // 3.0
如果你需要一个实际的Int,那么只需将其转换为一个(但前提是你确定Double不会大于Int.max):
let myInt = Int(myDouble.rounded())

注意事项

  • 本答案已完全重写。我的旧答案涉及C数学函数,如roundlroundfloorceil。然而,现在Swift已经内置了这些功能,我不能再推荐使用那些函数了。感谢@dfri提醒我。请查看@dfri的优秀答案。我还为四舍五入一个CGFloat做了类似的事情。

Int(myDouble.rounded()) <--- 如果 double 类型的值无法转换为 Int 类型,这段代码可能会抛出异常。 - Toad
@Toad,你确定吗?我在文档中没有看到这个。 - Suragch
我刚刚解决了一个生产中的崩溃问题,就是这个确切的问题。但即使我错了,它也不会崩溃,但对于双精度数大于最大整数仍会给出意外的结果。 - Toad
1
@Toad,没错,说得好,谢谢。我已经在答案里加了一条注释。 - Suragch
lround() 这个函数比 Int(myDouble.rounded()) 感觉更好一些。 - undefined

134

Swift 3 & 4 - 利用FloatingPoint协议中蓝图化的rounded(_:)方法

FloatingPoint协议(例如DoubleFloat符合该协议)蓝图化了rounded(_:)方法

func rounded(_ rule: FloatingPointRoundingRule) -> Self

其中 FloatingPointRoundingRule 是一个枚举,列举了许多不同的舍入规则:

case awayFromZero

四舍五入到最接近的允许值,其大小大于或等于源值。

case down

向下舍入到最接近的允许值,小于或等于源值。

case toNearestOrAwayFromZero

四舍五入到最接近的允许值;如果两个值同样接近,则选择具有更大大小的值。

case toNearestOrEven

四舍五入到最接近的允许值;如果两个值同样接近,则选择偶数。

case towardZero

向最接近的允许值舍入,其大小小于或等于源值。

case up

向上舍入到最接近的允许值,大于或等于源值。

We make use of similar examples to the ones from @Suragch's excellent answer to show these different rounding options in practice.

.awayFromZero

四舍五入到最接近的允许值,其大小大于或等于源值; 在C函数中没有直接相当的选项,因为这使用了条件性的ceilfloor,对于正数和负数的self,分别使用。

3.000.rounded(.awayFromZero) // 3.0
3.001.rounded(.awayFromZero) // 4.0
3.999.rounded(.awayFromZero) // 4.0

(-3.000).rounded(.awayFromZero) // -3.0
(-3.001).rounded(.awayFromZero) // -4.0
(-3.999).rounded(.awayFromZero) // -4.0

.down

相当于 C 语言中的 floor 函数。

3.000.rounded(.down) // 3.0
3.001.rounded(.down) // 3.0
3.999.rounded(.down) // 3.0

(-3.000).rounded(.down) // -3.0
(-3.001).rounded(.down) // -4.0
(-3.999).rounded(.down) // -4.0

.toNearestOrAwayFromZero

等同于 C 语言中的 round 函数。

3.000.rounded(.toNearestOrAwayFromZero) // 3.0
3.001.rounded(.toNearestOrAwayFromZero) // 3.0
3.499.rounded(.toNearestOrAwayFromZero) // 3.0
3.500.rounded(.toNearestOrAwayFromZero) // 4.0
3.999.rounded(.toNearestOrAwayFromZero) // 4.0

(-3.000).rounded(.toNearestOrAwayFromZero) // -3.0
(-3.001).rounded(.toNearestOrAwayFromZero) // -3.0
(-3.499).rounded(.toNearestOrAwayFromZero) // -3.0
(-3.500).rounded(.toNearestOrAwayFromZero) // -4.0
(-3.999).rounded(.toNearestOrAwayFromZero) // -4.0

这个取整规则也可以通过零参数rounded()方法来访问。
3.000.rounded() // 3.0
// ...

(-3.000).rounded() // -3.0
// ...

.toNearestOrEven

将数值四舍五入到最接近的允许值;如果两个值距离相等,则选择偶数;相当于 C 语言中的 rint 函数(非常类似于 nearbyint)。

3.499.rounded(.toNearestOrEven) // 3.0
3.500.rounded(.toNearestOrEven) // 4.0 (up to even)
3.501.rounded(.toNearestOrEven) // 4.0

4.499.rounded(.toNearestOrEven) // 4.0
4.500.rounded(.toNearestOrEven) // 4.0 (down to even)
4.501.rounded(.toNearestOrEven) // 5.0 (up to nearest)

.towardZero

相当于 C 语言的 trunc 函数。

3.000.rounded(.towardZero) // 3.0
3.001.rounded(.towardZero) // 3.0
3.999.rounded(.towardZero) // 3.0

(-3.000).rounded(.towardZero) // 3.0
(-3.001).rounded(.towardZero) // 3.0
(-3.999).rounded(.towardZero) // 3.0

如果舍入的目的是为了准备使用整数(例如在舍入后使用Int通过Double(或Float等)初始化),我们可以简单地利用这样一个事实:当使用Double(或Float等)初始化Int时,小数部分将被截断。
Int(3.000) // 3
Int(3.001) // 3
Int(3.999) // 3

Int(-3.000) // -3
Int(-3.001) // -3
Int(-3.999) // -3

.up

等同于 C 语言中的 ceil 函数。

3.000.rounded(.up) // 3.0
3.001.rounded(.up) // 4.0
3.999.rounded(.up) // 4.0

(-3.000).rounded(.up) // 3.0
(-3.001).rounded(.up) // 3.0
(-3.999).rounded(.up) // 3.0

补充说明:访问FloatingPoint源代码以验证C函数等效性到不同的FloatingPointRoundingRule规则

如果需要,我们可以查看FloatingPoint协议的源代码,直接查看C函数相当于公共FloatingPointRoundingRule规则。

swift/stdlib/public/core/FloatingPoint.swift.gyb中可以看出,rounded(_:)方法的默认实现使用可变的round(_:)方法:

public func rounded(_ rule: FloatingPointRoundingRule) -> Self {
    var lhs = self
    lhs.round(rule)
    return lhs
}

swift/stdlib/public/core/FloatingPointTypes.swift.gyb中,我们可以找到round(_:)的默认实现,在其中,FloatingPointRoundingRule规则与C舍入函数之间的等价性显而易见:

public mutating func round(_ rule: FloatingPointRoundingRule) {
    switch rule {
    case .toNearestOrAwayFromZero:
        _value = Builtin.int_round_FPIEEE${bits}(_value)
    case .toNearestOrEven:
        _value = Builtin.int_rint_FPIEEE${bits}(_value)
    case .towardZero:
        _value = Builtin.int_trunc_FPIEEE${bits}(_value)
    case .awayFromZero:
        if sign == .minus {
            _value = Builtin.int_floor_FPIEEE${bits}(_value)
        }
        else {
            _value = Builtin.int_ceil_FPIEEE${bits}(_value)
        }
    case .up:
        _value = Builtin.int_ceil_FPIEEE${bits}(_value)
    case .down:
        _value = Builtin.int_floor_FPIEEE${bits}(_value)
    }
}

@iosMentalist 谢谢您的迅速回复,我已经更新了答案的标题。 - dfrib
如果我想要任何方程式,例如3.0 = 3,3.1 = 3.5,3.4 = 3.5,3.6 = 4,3.9 - 4。 - PJR

7
**In Swift**

var a = 14.123456789
var b = 14.123456789
var c = 14.123456789
var d = 14.123456789
var e = 14.123456789
var f = 14.123456789

a.rounded(.up)                      //15
b.rounded(.down)                    //14
c.rounded(.awayFromZero)            //15
d.rounded(.towardZero)              //14
e.rounded(.toNearestOrAwayFromZero) //14
f.rounded(.toNearestOrEven)         //14

6

Swift 3: 如果你想将数字舍入到特定的数字位数,比如5.678434 -> 5.68,你可以将round()或roundf()函数与乘法结合使用:

let value:Float = 5.678434
let roundedValue = roundf(value * 100) / 100
print(roundedValue) //5.68

5
您可以按照以下方式扩展Swift 3中的FloatingPoint:
extension FloatingPoint {
    func rounded(to n: Int) -> Self {
        let n = Self(n)
        return (self / n).rounded() * n

    }
}

324.0.rounded(to: 5)   // 325

请问您能解释一下吗?Self是什么意思? - JZAU
@Jacky Self 指的是类 FloatingPoint,而 self 指的是该类的实例。 - George Yacoub
@GeorgeYacoub 中的 Self 指的是符合 FloatingPoint 的类型(在该示例中使用的是 Double),但它们是结构体,而不是类。 - Leo Dabus

3

Swift 3

var myNum = 8.09
myNum.rounded() // result = 8 and leaves myNum unmodified

不错。我之前不知道这个方法。需要注意的一点是,myNum.rounded() 不会改变 myNum 的值,但 myNum.round() 会改变它的值。 - Suragch
@Suragch,我已经编辑了答案以反映您的评论。 - Adil Hussain

-1

对我来说,一个非常简单的解决方案起了作用:

  if (62 % 50 != 0) {
      var number = 62 / 50 + 1 // adding 1 is doing the actual "round up"
  }

number 包含值 2


使用 floatValue.rounded() 默认函数。 - Naresh
这根本没有解决问题,因为根本没有使用任何浮点数,所有的计算都是用Int来完成的。对于许多情况来说,它也会产生错误的值,这取决于你对"round"的期望(例如,62.0/50.0四舍五入应该是1.0)。这更接近于"ceiling",即大于给定值的下一个整数值。但即使如此,它也是不正确的,因为50.0/50.0的ceiling是1.0,但你的方法会得到2.0。 - undefined

-2

这是我遇到的最简单的方法

let weightValue = 143.69
print("Rounded off weight value = \(Int(weightValue))")

另一种方法:

print("Rounded off weight value = \((String(format: "%.0f", sender.value)))")

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