在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个回答

17

使用内置的Foundation Darwin库

SWIFT 3

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

使用方法:

let number:Double = 12.987654321
print(number.round(to: 3)) 

输出:12.988


17
如果您想对Double类型的值进行四舍五入,建议使用Swift中的Decimal类型,以避免在对这些四舍五入后的值进行数学操作时引入任何错误。如果使用Decimal类型,它可以准确地表示该四舍五入浮点值的小数部分。
因此,您可以这样做:
extension Double {
    /// Convert `Double` to `Decimal`, rounding it to `scale` decimal places.
    ///
    /// - Parameters:
    ///   - scale: How many decimal places to round to. Defaults to `0`.
    ///   - mode:  The preferred rounding mode. Defaults to `.plain`.
    /// - Returns: The rounded `Decimal` value.

    func roundedDecimal(to scale: Int = 0, mode: NSDecimalNumber.RoundingMode = .plain) -> Decimal {
        var decimalValue = Decimal(self)
        var result = Decimal()
        NSDecimalRound(&result, &decimalValue, scale, mode)
        return result
    }
}

然后,您可以像这样获得四舍五入的Decimal值:

let foo = 427.3000000002
let value = foo.roundedDecimal(to: 2) // results in 427.30

如果您想要在特定的小数位数下显示它(以及本地化用户当前语言环境的字符串),您可以使用NumberFormatter

let formatter = NumberFormatter()
formatter.maximumFractionDigits = 2
formatter.minimumFractionDigits = 2

if let string = formatter.string(for: value) {
    print(string)
}

3
到目前为止,这是最佳答案。 - Codetard
(4.81).roundedDecimal(to: 2, mode: .down) will return 4.8 instead of 4.81, use Decimal(string: self.description)! for a precise decimalValue - vk.edward.li
3
@vk.edward.li - 是的,对于小数的四舍五入应该谨慎,因为像4.81这样的值使用Double无法准确表示(它变成了4.8099999999999996)。但是我建议不要使用description(因为其采用的舍入方式未记录,并且可能会更改)。如果您真的想进行小数精确计算,应完全避免使用Double,而应直接使用Decimal(例如,Decimal(sign: .plus, exponent: -2, significand: 481)Decimal(string: "4.81")),但不要使用description - Rob
@Rob 通常情况下,如果将“4.81”存储为Double,则无法获取其字符串值,因此您必须使用self.description(修改后的Grisu2算法)来修复已经丢失的精度。 - vk.edward.li
1
我理解你的观点,但我不同意你的解决方案。description 方法的舍入行为既没有记录也没有保证。此外,它也不是本地化的。在我看来,除了用于日志记录之外,使用 description 进行其他任何操作都是错误的。我要么始终使用 Decimal,避免使用 Double,要么只使用不同的舍入模式。或者,如果必须使用 description,则使用自己的本地化方法,该方法使用 Grisu 算法(但当您知道要舍入到多少位小数时,这似乎是不必要的)。但我会避免依赖 description 的这种附属行为。 - Rob

13

一种方便的方法可以是使用类型为Double扩展功能

extension Double {
    var roundTo2f: Double {return Double(round(100 *self)/100)  }
    var roundTo3f: Double {return Double(round(1000*self)/1000) }
}

用法:

let regularPie:  Double = 3.14159
var smallerPie:  Double = regularPie.roundTo3f  // results 3.142
var smallestPie: Double = regularPie.roundTo2f  // results 3.14

12

如果您的需求稍微复杂,这是一种有用的长期解决方案。您可以在Swift中使用数字格式化工具。

let numberFormatter: NSNumberFormatter = {
    let nf = NSNumberFormatter()
    nf.numberStyle = .DecimalStyle
    nf.minimumFractionDigits = 0
    nf.maximumFractionDigits = 1
    return nf
}()

假设你想要打印的变量是

var printVar = 3.567

这将确保以所需格式返回:

numberFormatter.StringFromNumber(printVar)
因此,这里的结果将是“3.6”(四舍五入)。虽然这不是最经济的解决方案,但我提供它是因为OP提到了打印(在这种情况下,字符串不是不受欢迎的),并且因为这个类允许设置多个参数。

1
一个快速的提示是,任何格式化程序在编程上都相当昂贵,因此不建议用于需要快速或重复执行的任务。 - Jonathan Thornton

10
要么:
  1. 使用 String(format:)

    • Double 强制转换为带有 %.3f 格式说明符的 String,然后再转换回 Double

      Double(String(format: "%.3f", 10.123546789))!
      
    • 或者扩展Double以处理N位小数:

      extension Double {
          func rounded(toDecimalPlaces n: Int) -> Double {
              return Double(String(format: "%.\(n)f", self))!
          }
      }
      
  2. 通过计算

    • 乘以10^3,四舍五入,然后除以10^3...

  3. (1000 * 10.123546789).rounded()/1000
    
  4. 或者扩展 Double 以处理 N 位小数:

  5. extension Double {    
        func rounded(toDecimalPlaces n: Int) -> Double {
            let multiplier = pow(10, Double(n))
            return (multiplier * self).rounded()/multiplier
        }
    }
    

8

为了方便使用,我创建了一个扩展:

extension Double {
    var threeDigits: Double {
        return (self * 1000).rounded(.toNearestOrEven) / 1000
    }
    
    var twoDigits: Double {
        return (self * 100).rounded(.toNearestOrEven) / 100
    }
    
    var oneDigit: Double {
        return (self * 10).rounded(.toNearestOrEven) / 10
    }
}

var myDouble = 0.12345
print(myDouble.threeDigits)
print(myDouble.twoDigits)
print(myDouble.oneDigit)

打印结果如下:
0.123
0.12
0.1

感谢其他答案的启示!


8
我会使用:
print(String(format: "%.3f", totalWorkTimeInHours))

并将.3f更改为您需要的任何小数位数


他想要打印出3位数,而不是以NSTimeInterval的形式获取它。 - Mohsen Hosseinpour

8

将双精度值四舍五入到小数点后x位
小数点后的位数

var x = 1.5657676754
var y = (x*10000).rounded()/10000
print(y)  // 1.5658 

var x = 1.5657676754 
var y = (x*100).rounded()/100
print(y)  // 1.57 

var x = 1.5657676754
var y = (x*10).rounded()/10
print(y)  // 1.6

这段代码在做什么?这三个例子展示了什么? - rene
1
仅提供代码答案是没有帮助的,请尝试解释每个示例的作用。 - Mo Abdul-Hameed
这段代码可以运行,但是在结尾会留下一串零。 - user3069232

8

这是一种更灵活的保留N位有效数字的算法

Swift 3解决方案

extension Double {
// Rounds the double to 'places' significant digits
  func roundTo(places:Int) -> Double {
    guard self != 0.0 else {
        return 0
    }
    let divisor = pow(10.0, Double(places) - ceil(log10(fabs(self))))
    return (self * divisor).rounded() / divisor
  }
}


// Double(0.123456789).roundTo(places: 2) = 0.12
// Double(1.23456789).roundTo(places: 2) = 1.2
// Double(1234.56789).roundTo(places: 2) = 1200

7
最好的方式是使用苹果预定义的方法来格式化双精度属性。
mutating func round(_ rule: FloatingPointRoundingRule)

FloatingPointRoundingRule是一个枚举类型,具有以下可能性:
枚举案例:
awayFromZero - 四舍五入到最接近且大于等于原始值的允许值。
down - 四舍五入到最接近且小于等于原始值的允许值。
toNearestOrAwayFromZero - 四舍五入到最接近的允许值;如果两个值相等,则选择较大的那个值。
toNearestOrEven - 四舍五入到最接近的允许值;如果两个值相等,则选择偶数值。
towardZero - 四舍五入到最接近且小于等于原始值的允许值。 up - 四舍五入到最接近且大于等于原始值的允许值。
var aNumber : Double = 5.2
aNumber.rounded(.up) // 6.0

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