在Swift中将双精度值舍入到x位小数

545

有人可以告诉我在Swift中如何将double值舍入到x位小数吗?

我有:

var totalWorkTimeInHours = (totalWorkTime/60/60)

使用 totalWorkTime 表示时间长度,数据类型为 NSTimeInterval (double)(以秒为单位)。

totalWorkTimeInHours 给出的是小时数,但它会给出一个非常长的精确数字,例如 1.543240952039......

当我打印 totalWorkTimeInHours 时,如何将其四舍五入到 1.543 这样的值?


2
请查看我的答案,以找到使用Darwin round(_:)Double round()NSString初始化器、String初始化器、NumberFormatter、Double扩展甚至NSDecimalNumberDecimal舍入双精度的多达9种不同的方法。 - Imanou Petit
@Rounded,一个Swift 5.1属性包装器:https://gist.github.com/abhijithpp/1cc41b41a5d1c8f007da90f20bc0c65f - Abhijith
36个回答

526
你可以使用Swift的round函数来完成这个任务。
要将一个Double保留3位小数进行四舍五入,首先将其乘以1000,然后进行四舍五入,并将结果除以1000:
let x = 1.23556789
let y = Double(round(1000 * x) / 1000)
print(y) /// 1.236

与任何类型的printf(...)String(format: ...)解决方案不同,此操作的结果仍然是Double类型。
关于有时无法正常工作的评论,请阅读:程序员应了解的浮点数算术知识

1
由于某些原因它没有起到作用... 我在以下情况下使用它 TransformOf<Double, String>(fromJSON: {Double(round(($0! as NSString).doubleValue * 100)/100)}, toJSON: {"($0)"}) - Fawkes
41
在 playground 中无效:let rate = 9.94999999999; Double(round(rate * 100) / 100);输出结果为:9.94999999999, 9.949999999999999这只适用于打印,但我该如何将其赋值给变量? - Alexander Yakovlev
1
你应该使用Decimal而不是普通的Doubles。 - csotiriou
34
“Every Computer Scientist Should Know About Floating-Point Arithmetic”一书共73页,能否简述一下主要内容? :) 该书涵盖了计算机科学家在浮点数处理方面应该了解的关键知识。简而言之,它介绍了计算机浮点数的表示方式、舍入误差、算术运算和常见问题,并提供了实用建议以及如何避免常见的陷阱和错误。 - JaredH
2
@HardikDG 我不记得了,抱歉 :-) - Alexander Yakovlev
显示剩余4条评论

492

Swift 2的扩展程序

下面是一个更通用的解决方案,适用于Swift 2和iOS 9:

extension Double {
    /// Rounds the double to decimal places value
    func roundToPlaces(places:Int) -> Double {
        let divisor = pow(10.0, Double(places))
        return round(self * divisor) / divisor
    }
}

适用于Swift 3的扩展

在Swift 3中,round已被rounded取代:

extension Double {
    /// Rounds the double to decimal places value
    func rounded(toPlaces places:Int) -> Double {
        let divisor = pow(10.0, Double(places))
        return (self * divisor).rounded() / divisor
    }
}


返回四舍五入保留4位小数的Double示例:

let x = Double(0.123456789).roundToPlaces(4)  // x becomes 0.1235 under Swift 2
let x = Double(0.123456789).rounded(toPlaces: 4)  // Swift 3 version

32
Swift的Double类型的特性是可能会存在精度问题 - 因为它在二进制中存储和访问的方式,即使使用四舍五入函数也无法返回一个精确到3位小数的数字,例如:round(8.46 * pow(10.0, 3.0)) / pow(10.0, 3.0)返回8.460000000000001。在相同情境下,使用Float类型似乎可以工作:round(Float(8.46) * pow(10.0, 3.0)) / pow(10.0, 3.0)得到8.46。这只是值得注意的一点。 - Ben Saufley
3
很不幸,这个在 Swift 3 中已经不再适用了,我收到了这个错误 No '*' candidates produce the expected contextual result type 'FloatingPointRoundingRule' - koen
3
我更新了解决方案并添加了适用于Swift 3的扩展。 - Sebastian
3
为了让您的 Swift 3 代码更加流畅,您应该将函数更改为:func rounded(toPlaces places: Int) -> Double - FouZ
3
但是如果你输入98.68,它也不能正常工作。这种情况下,我得到的是98.6800003。 - Alexander Khitev
显示剩余6条评论

482

当我打印totalWorkTimeInHours时,如何将其舍入为1.543呢?

要将totalWorkTimeInHours舍入到3位小数以进行打印,请使用带有format字符串的String构造函数:

print(String(format: "%.3f", totalWorkTimeInHours))

36
这是截断,而不是四舍五入。 - Eyeball
49
@Eyeball:实际上,它会四舍五入。这与C语言的printf功能的行为相同,会将数字四舍五入到指定的位数。因此,String(format: "%.3f", 1.23489)会输出1.235 - zisoft
9
原帖作者正在寻找可用的数值,而非用于显示的字符。 - pr1001
33
@pr1001,也许是这样,但当时不太清楚,46个人发现我的回答很有用。Stack Overflow不仅仅是帮助提问者。 - vacawama
4
奇怪,每次都返回 0.00。 - Eric H
显示剩余7条评论

378

利用Swift 5,根据您的需要,您可以选择以下9种风格之一,以使从Double获得圆角结果。


#1.使用FloatingPointrounded()方法

在最简单的情况下,您可以使用Double rounded()方法。

let roundedValue1 = (0.6844 * 1000).rounded() / 1000
let roundedValue2 = (0.6849 * 1000).rounded() / 1000
print(roundedValue1) // returns 0.684
print(roundedValue2) // returns 0.685

#2. 使用 FloatingPointrounded(_:) 方法


let roundedValue1 = (0.6844 * 1000).rounded(.toNearestOrEven) / 1000
let roundedValue2 = (0.6849 * 1000).rounded(.toNearestOrEven) / 1000
print(roundedValue1) // returns 0.684
print(roundedValue2) // returns 0.685

#3. 使用 Darwin 的 round 函数

Foundation 通过 Darwin 提供了一个 round 函数。

import Foundation

let roundedValue1 = round(0.6844 * 1000) / 1000
let roundedValue2 = round(0.6849 * 1000) / 1000
print(roundedValue1) // returns 0.684
print(roundedValue2) // returns 0.685

#4. 使用由 Darwin 的 round 和 pow 函数构建的 Double 扩展自定义方法

如果您需要多次重复执行先前的操作,重构代码可能是一个不错的选择。

import Foundation

extension Double {
    func roundToDecimal(_ fractionDigits: Int) -> Double {
        let multiplier = pow(10, Double(fractionDigits))
        return Darwin.round(self * multiplier) / multiplier
    }
}

let roundedValue1 = 0.6844.roundToDecimal(3)
let roundedValue2 = 0.6849.roundToDecimal(3)
print(roundedValue1) // returns 0.684
print(roundedValue2) // returns 0.685

#5. 使用 NSDecimalNumberrounding(accordingToBehavior:) 方法

如果需要,NSDecimalNumber 提供了一个冗长但功能强大的解决方案来对十进制数进行四舍五入。

import Foundation

let scale: Int16 = 3

let behavior = NSDecimalNumberHandler(roundingMode: .plain, scale: scale, raiseOnExactness: false, raiseOnOverflow: false, raiseOnUnderflow: false, raiseOnDivideByZero: true)

let roundedValue1 = NSDecimalNumber(value: 0.6844).rounding(accordingToBehavior: behavior)
let roundedValue2 = NSDecimalNumber(value: 0.6849).rounding(accordingToBehavior: behavior)

print(roundedValue1) // returns 0.684
print(roundedValue2) // returns 0.685

#6. 使用 NSDecimalRound(_:_:_:_:) 函数
import Foundation

let scale = 3

var value1 = Decimal(0.6844)
var value2 = Decimal(0.6849)

var roundedValue1 = Decimal()
var roundedValue2 = Decimal()

NSDecimalRound(&roundedValue1, &value1, scale, NSDecimalNumber.RoundingMode.plain)
NSDecimalRound(&roundedValue2, &value2, scale, NSDecimalNumber.RoundingMode.plain)

print(roundedValue1) // returns 0.684
print(roundedValue2) // returns 0.685

#7. 使用 NSStringinit(format:arguments:) 初始化器

如果你想要从你的四舍五入操作返回一个 NSString,使用 NSString 的初始化器是一个简单而有效的解决方案。

import Foundation

let roundedValue1 = NSString(format: "%.3f", 0.6844)
let roundedValue2 = NSString(format: "%.3f", 0.6849)
print(roundedValue1) // prints 0.684
print(roundedValue2) // prints 0.685

#8. 使用 Stringinit(format:_:) 初始化器

Swift的 String 类型是与 Foundation 的 NSString 类相桥接的。因此,您可以使用以下代码来从您的舍入操作中返回一个 String

import Foundation

let roundedValue1 = String(format: "%.3f", 0.6844)
let roundedValue2 = String(format: "%.3f", 0.6849)
print(roundedValue1) // prints 0.684
print(roundedValue2) // prints 0.685

#9. 使用 NumberFormatter

如果您希望从舍入操作中获取一个String?NumberFormatter提供了一个高度可定制的解决方案。

import Foundation

let formatter = NumberFormatter()
formatter.numberStyle = NumberFormatter.Style.decimal
formatter.roundingMode = NumberFormatter.RoundingMode.halfUp
formatter.maximumFractionDigits = 3

let roundedValue1 = formatter.string(from: 0.6844)
let roundedValue2 = formatter.string(from: 0.6849)
print(String(describing: roundedValue1)) // prints Optional("0.684")
print(String(describing: roundedValue2)) // prints Optional("0.685")

3
很好的回答。对于第五条建议,我可能会建议您考虑使用Decimal,它可以无缝桥接到NSDecimalNumberDecimal是Swift的本地类型,并为本地运算符(如+-等)提供更优雅的支持。 - Rob
1
使用 #9 选项将保证您期望固定的小数位数(精度数字数量),比如十进制数 43.12345 和小数位数 = 6,您将得到 43.123450。 - Almas Adilbek
1
@ImanouPetit 谢谢您发布这个。我可以删除一些扩展程序。 - Adrian
6
我很遗憾NumberFormatter只是呈现数字给用户的最后一个选择,它应该是第一个选择。没有其他方法可以生成适当本地化的数字。 - Sulthan
1
NumberFormatter的极高可定制性对于我非常有帮助!很高兴我读到了你回答的最后一行。 - Carl Smith
显示剩余3条评论

203

在Swift 5.5和Xcode 13.2中:

let pi: Double = 3.14159265358979
String(format:"%.2f", pi)

示例:

Rounding a double value

PS .:自从Swift 2.0和Xcode 7.2以来,它仍然保持不变。

看了你的代码后,我做了一个小修改,现在它可以正常工作了:扩展 Float { var toString: String { return String(format: "%.2f", self) } } - Sanjeevcn
我在一个字符串变量上使用它,该变量用于构成一个长字符串表达式,并在SwiftUI Text视图上显示。非常感谢。 - Fortran programmer
为什么你一直在更新版本号?你发布的这两行代码多年来一直有效,并且与当前显示的版本无关。 - undefined

61

这是完整的可用代码

适用于Swift 3.0/4.0/5.0,Xcode 9.0 GM/9.2及以上版本

let doubleValue : Double = 123.32565254455
self.lblValue.text = String(format:"%.f", doubleValue)
print(self.lblValue.text)

输出 - 123

let doubleValue : Double = 123.32565254455
self.lblValue_1.text = String(format:"%.1f", doubleValue)
print(self.lblValue_1.text)

输出 - 123.3

let doubleValue : Double = 123.32565254455
self.lblValue_2.text = String(format:"%.2f", doubleValue)
print(self.lblValue_2.text)

输出 - 123.33

let doubleValue : Double = 123.32565254455
self.lblValue_3.text = String(format:"%.3f", doubleValue)
print(self.lblValue_3.text)

输出 - 123.326


2
一行?似乎是三行 ;) - Petter Friberg
你不能使用字符串进行计算,我认为这不是正确的答案,因为它只用于显示。 - JBarros35

35
在Yogi的回答基础上,这里有一个能完成任务的Swift函数:
func roundToPlaces(value:Double, places:Int) -> Double {
    let divisor = pow(10.0, Double(places))
    return round(value * divisor) / divisor
}

作为带有舍入规则的扩展:
extension Double {
    /// - returns: Rounded value with specific round rule and precision
    func roundToPlaces(_ rule: FloatingPointRoundingRule? = . toNearestOrEven, precision: Int) -> Double {
        let divisor = pow(10.0, Double(precision))
        return (self * divisor).rounded(rule) / divisor
    }
}

print(0.123456.roundToPlaces(.down, precision: 4)) // 0.1234
print(0.123456.roundToPlaces(.up, precision: 4)) // 0.1235

31

在Swift 3.0和Xcode 8.0中:

extension Double {
    func roundTo(places: Int) -> Double {
        let divisor = pow(10.0, Double(places))
        return (self * divisor).rounded() / divisor
    }
}

使用此扩展程序的方式如下:

let doubleValue = 3.567
let roundedValue = doubleValue.roundTo(places: 2)
print(roundedValue) // prints 3.56

26

Swift 4,Xcode 10

yourLabel.text =  String(format:"%.2f", yourDecimalValue)

2
我没有点踩,但我认为原因可能是因为这返回的是数字的字符串表示形式,而不是数字本身。如果这是你所需要的,那么没问题,但如果你想要将数字本身四舍五入以供进一步使用,那么你可能需要这个数字。OP确实说了“当我打印时”,所以我认为在这种情况下,这是一个可以接受、简单和干净的解决方案。 - Ben Stahl
1
完美。一行代码解决。我只需要将小数值显示为字符串在我的标签中。 - zeeshan

20

保留小数点后特定位的代码如下:

var a = 1.543240952039
var roundedString = String(format: "%.3f", a)

这里的%.3f告诉Swift将此数字四舍五入保留3位小数。如果您想要双精度数,可以使用以下代码:

// 字符串转双精度浮点数

var roundedString = Double(String(format: "%.3f", b))


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