快速将双精度值四舍五入到x小数位数


395

谁能告诉我在Swift中如何将双精度值四舍五入到小数点后位数x?

我有:

var totalWorkTimeInHours = (totalWorkTime/60/60)

totalWorkTime秒为单位的NSTimeInterval(double)。

totalWorkTimeInHours 会给我时间,但是会给我这么长的时间,例如1.543240952039 ...

打印时如何将其四舍五入为1.543 totalWorkTimeInHours


1
我的答案找到多达9点不同的方式来圆一个使用达尔文双round(_:)Double round()NSString初始化,String初始化,NumberFormatter,双扩展名,甚至NSDecimalNumberDecimal
伊曼努·佩蒂特

@Rounded,一个快速的5.1属性包装器:gist.github.com/abhijithpp/1cc41b41a5d1c8f007da90f20bc0c65f
Abhijith

Answers:


389

您可以使用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

编辑:
关于有时不起作用的评论,请阅读:

每个程序员应该了解的浮点运算法则


由于某些原因,它无济于事。我在以下情况下使用TransformOf <Double,String>(fromJSON:{Double(round(($$!as NSString).doubleValue * 100)/ 100)},toJSON: {“($ 0)”})
福克斯2015年

29
在操场上对我不起作用:let rate = 9.94999999999; Double(round(rate * 100) / 100);输出:9.94999999999, 9.949999999999999仅适用于打印,但是如何将其分配给变量?
亚历山大·雅科夫列夫

您应该为此使用Decimal,而不是普通Double。
csotiriou

13
“每位计算机科学家都应了解的浮点运算法则”长达73页。什么是TL; DR?:)
JaredH '18

1
@HardikDG我不记得了,对不起:-)
Alexander Yakovlev

430

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
    }
}


返回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

17
斯威夫特的双重性质似乎是,它可以是不精确的-因为这样它的存储/二进制访问,即使使用这种舍入函数将不会返回一个数字四舍五入至小数点后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)yields 8.46。只是一些值得注意的东西。
Ben Saufley

2
不幸的是,这在Swift 3中不再起作用,我得到了这个错误No '*' candidates produce the expected contextual result type 'FloatingPointRoundingRule'
koen 16'Aug

2
我更新了解决方案,并为Swift 3添加了扩展。–
Sebastian

2
要更迅速3 ...您应该更改为四舍五入函数(toPlaces place:Int)-> Double
FouZ

2
@BenSaufley但是,如果您输入98.68,它也不起作用。在这种情况下,我得到98.6800003
Alexander Khitev

340

打印时 如何将其四舍五入为1.543 totalWorkTimeInHours

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

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

24
那是截断,而不是舍入
眼球

33
@Eyeball:实际上,它确实是圆的。它与C printf功能的行为相同,后者四舍五入到所请求的数字。所以String(format: "%.3f", 1.23489)打印1.235
zisoft 2014年

6
原始张贴者正在寻找要使用的数字值,而不是要显示的字符串。
pr1001 '16

17
@ pr1001,也许是这样,但是当时还不清楚,还有46个其他人认为我的回答很有用。堆栈溢出不仅仅是帮助OP。
vacawama '16

3
奇怪的是,这每次都返回0.00。
埃里克·H

254

使用Swift 5时,您可以根据需要选择以下9种样式之一,以从中获得四舍五入的结果Double


#1。使用FloatingPoint rounded()方法

在最简单的情况下,您可以使用该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。使用FloatingPoint rounded(_:)方法

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。使用达尔文round功能

Foundation round通过Darwin 提供功能。

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。使用由DoubleDarwin roundpow函数构建的扩展自定义方法

如果您想多次重复之前的操作,那么重构代码可能是一个好主意。

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。使用NSDecimalNumber rounding(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。使用NSString init(format:arguments:)初始化

如果NSString要从舍入操作返回a ,则使用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。使用String init(format:_:)初始化

Swift的String类型与Foundation的NSString类联系在一起。因此,可以使用以下代码来String从舍入操作返回a :

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")

1
好答案。我可能会建议您在#5上考虑使用Decimal,这是免费电话桥接到NSDecimalNumberDecimal是Swift的本机类型,并且为本机运算符+-,等)提供了更优雅的支持
Rob

@Rob我已经用NSDecimalRound(_:_:_:_:)功能更新了答案。谢谢。
Imanou Petit

使用#9选择将保证您期望固定的分数(精确数字的位数),比方说十进制数43.12345和分数= 6,您将得到43.123450
Almas Adilbek,

1
@ImanouPetit感谢您发布此内容。我可以扩展几个扩展名。
阿德里安

很遗憾,这NumberFormatter是最后的选择,而它应该是向用户提供数字的第一个选择。没有其他方法可以生成正确的本地化数字。
苏珊(Sulthan)


50

这是一个完全有效的代码

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


1
一条线?,似乎是3条线;)
Petter Friberg

您无法使用字符串来计算事物,我认为它不是正确的答案,因为它仅用于显示。
JBarros35

25

在Yogi的答案的基础上,这是完成任务的Swift函数:

func roundToPlaces(value:Double, places:Int) -> Double {
    let divisor = pow(10.0, Double(places))
    return round(value * divisor) / divisor
}

20

在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

19

斯威夫特4,Xcode 10

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

1
我没有拒绝投票,但我认为原因可能是因为这是返回数字而不是数字的字符串表示形式。如果您只需要这些,那很好,但是您可能希望数字本身四舍五入以备将来使用。OP确实说过“当我打印时”,因此在这种情况下,我确实认为这是一个可以接受,轻松且干净的解决方案。
Ben Stahl

1
完善。一线解决方案。我只需要在标签中将十进制值显示为字符串即可。
zeeshan

18

小数点后特定数字的代码为:

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

这里的%.3f告诉swift将这个数字四舍五入到小数点后三位。如果要双精度数,可以使用以下代码:

//字符串加倍

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


14

使用内置的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


9

一种方便的方法是使用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

9

如果要舍入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)
}

2
迄今为止最好的答案。
Codetard

(4.81).roundedDecimal(to: 2, mode: .down)将返回4.8而不是4.81,Decimal(string: self.description)!用于精确decimalValue
vk.edward.li

1
@ vk.edward.li-是的,应该警惕将小数点四舍五入,因为像4.81这样的值根本无法用精确表示Double(变成4.8099999999999996)。但是我建议您不要使用description(因为它使用的舍入没有记录,并且可能会发生变化)。如果你真的想要做小数点精确的数学,你应该避免Double完全,而是应该用Decimal直接(如Decimal(sign: .plus, exponent: -2, significand: 481)Decimal(string: "4.81")但不要用。description
罗布

如果将@Rob存储为Double,通常无法获得字符串值“ 4.81”,因此您必须使用self.description(改进的Grisu2算法)来修复已经丢失的精度
vk.edward.li

我明白你的意思。我只是不同意您的解决方案。description方法的舍入行为既没有记录也没有保证。另外,它还没有本地化。恕我直言,将其description用于日志记录以外的其他用途是错误的。我要么使用全文Decimal,避免Double完全使用,要么只是使用其他舍入模式。或者,如果需要,请使用自己的本地化方法,该方法使用Grisu风格的算法(但是,当您知道要舍入到小数点后几位时,这似乎是不必要的)。但我避免依赖于的这种辅助行为description
罗布

8

这是一个漫长的解决方法,如果您的需求稍微复杂一点,则可能会派上用场。您可以在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提到了打印(在这种情况下String是不希望的),并且因为此类允许设置多个参数。


1
快速说明一下,任何格式化程序在程序上都是相当昂贵的,因此对于需要快速执行或重复执行的任务不建议使用。
乔纳森·桑顿

7

我会用

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

并将.3f更改为所需的任何十进制数


他想用3位数字打印,而不是在NSTimeInterval中打印-Mohsen
Hossein

7

要么:

  1. 使用String(format:)

    • 使用格式说明符输入DoubleString%.3f然后返回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 ...

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

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

6

这是一种更灵活的舍入到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

6

格式化double属性的最佳方法是使用Apple预定义方法。

mutating func round(_ rule: FloatingPointRoundingRule)

FloatingPointRoundingRule是一个枚举,具有以下可能性

枚举案例:

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

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

case toNearestOrAwayFromZero舍 入到最接近的允许值;如果两个值相等,则选择一个较大的值。

case toNearestOrEven舍 入到最接近的允许值;如果两个值相等接近,则选择偶数。

朝向“零”的情况将其舍入 到其大小小于或等于源的大小的最接近的允许值。

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

var aNumber : Double = 5.2
aNumber.rounded(.up) // 6.0

6

将双精度值四舍五入为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

仅代码答案无济于事,请尝试解释这些示例中的每一个。

5

不是Swift,但是我敢肯定,您会明白的。

pow10np = pow(10,num_places);
val = round(val*pow10np) / pow10np;

4

这似乎在Swift 5中有效。

非常奇怪的是,目前还没有标准功能。

//从舍入到双精度小数到n个小数位

extension Double {

    func truncate(to places: Int) -> Double {
    return Double(Int((pow(10, Double(places)) * self).rounded())) / pow(10, Double(places))
    }

}

2

您可以添加此扩展名:

extension Double {
    var clean: String {
        return self.truncatingRemainder(dividingBy: 1) == 0 ? String(format: "%.0f", self) : String(format: "%.2f", self)
    }
}

并这样称呼它:

let ex: Double = 10.123546789
print(ex.clean) // 10.12

这是将其表示为String的简洁实现。我想知道是否有可能或合理地在句号之后仍将其保持为Double的情况下写出一个数字扩展表示形式。
thinkswift19年

2

为了避免浮点缺陷,请使用十进制

extension Float {
    func rounded(rule: NSDecimalNumber.RoundingMode, scale: Int) -> Float {
        var result: Decimal = 0
        var decimalSelf = NSNumber(value: self).decimalValue
        NSDecimalRound(&result, &decimalSelf, scale, rule)
        return (result as NSNumber).floatValue
    }
}

例如
1075.58
使用小数位数为2和.down时四舍五入为1075.57如果使用小数位数为2和.down的1075.58四舍五入为1075.58


1

我想知道是否有可能纠正用户的输入。也就是说,如果他们输入三个小数而不是两个小数,则表示一个美元金额。说1.111而不是1.11可以四舍五入吗?出于多种原因,答案是否定的!用钱超过0.001即最终会在真正的支票簿中引起问题。

这是一个功能,用于检查用户输入的数字是否超过了句点。但这将允许1.,1.1和1.11。

假定已经检查过该值以成功从String转换为Double。

//func need to be where transactionAmount.text is in scope

func checkDoublesForOnlyTwoDecimalsOrLess()->Bool{


    var theTransactionCharacterMinusThree: Character = "A"
    var theTransactionCharacterMinusTwo: Character = "A"
    var theTransactionCharacterMinusOne: Character = "A"

    var result = false

    var periodCharacter:Character = "."


    var myCopyString = transactionAmount.text!

    if myCopyString.containsString(".") {

         if( myCopyString.characters.count >= 3){
                        theTransactionCharacterMinusThree = myCopyString[myCopyString.endIndex.advancedBy(-3)]
         }

        if( myCopyString.characters.count >= 2){
            theTransactionCharacterMinusTwo = myCopyString[myCopyString.endIndex.advancedBy(-2)]
        }

        if( myCopyString.characters.count > 1){
            theTransactionCharacterMinusOne = myCopyString[myCopyString.endIndex.advancedBy(-1)]
        }


          if  theTransactionCharacterMinusThree  == periodCharacter {

                            result = true
          }


        if theTransactionCharacterMinusTwo == periodCharacter {

            result = true
        }



        if theTransactionCharacterMinusOne == periodCharacter {

            result = true
        }

    }else {

        //if there is no period and it is a valid double it is good          
        result = true

    }

    return result


}

1
//find the distance between two points
let coordinateSource = CLLocation(latitude: 30.7717625, longitude:76.5741449 )
let coordinateDestination = CLLocation(latitude: 29.9810859, longitude: 76.5663599)
let distanceInMeters = coordinateSource.distance(from: coordinateDestination)
let valueInKms = distanceInMeters/1000
let preciseValueUptoThreeDigit = Double(round(1000*valueInKms)/1000)
self.lblTotalDistance.text = "Distance is : \(preciseValueUptoThreeDigit) kms"


1

如果需要带数字值的Text元素,则这是SwiftUI的一个。

struct RoundedDigitText : View {
    let digits : Int
    let number : Double

    var body : some View {
        Text(String(format: "%.\(digits)f", number))
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.