Rounding a double value to x number of decimal pla

2018-12-31 21:50发布

问题:

Can anyone tell me how to round a double value to x number of decimal places in Swift?

I have:

var totalWorkTimeInHours = (totalWorkTime/60/60)

With totalWorkTime being an NSTimeInterval (double) in second.

totalWorkTimeInHours will give me the hours, but it gives me the amount of time in such a long precise number e.g. 1.543240952039......

How do I round this down to, say, 1.543 when I print totalWorkTimeInHours?

回答1:

You can use Swift\'s round function to accomplish this.

To round a Double with 3 digits precision, first multiply it by 1000, round it and divide the rounded result by 1000:

let x = 1.23556789
let y = Double(round(1000*x)/1000)
print(y)  // 1.236

Other than any kind of printf(...) or String(format: ...) solutions, the result of this operation is still of type Double.

EDIT:
Regarding the comments that it sometimes does not work, please read this:

What Every Computer Scientist Should Know About Floating-Point Arithmetic



回答2:

Extension for Swift 2

A more general solution is the following extension, which works with 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
    }
}


Extension for Swift 3

In Swift 3 round is replaced by 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
    }
}


Example which returns Double rounded to 4 decimal places:

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


回答3:

Use the String constructor which takes a format string:

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


回答4:

With Swift 4.2, according to your needs, you can choose one of the 9 following styles in order to have a rounded result from a Double.


#1. Using FloatingPoint rounded() method

In the simplest case, you may use the Double round() method.

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. Using FloatingPoint rounded(_:) method

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

#3. Using Darwin round function

Foundation offers a round function via 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. Using a Double extension custom method builded with Darwin round and pow functions

If you want to repeat the previous operation many times, refactoring your code can be a good idea.

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. Using NSDecimalNumber rounding(accordingToBehavior:) method

If needed, NSDecimalNumber offers a verbose but powerful solution for rounding decimal numbers.

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. Using NSDecimalRound(_:_:_:_:) function

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. Using NSString init(format:arguments:) initializer

If you want to return a NSString from your rounding operation, using NSString initializer is a simple but efficient solution.

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. Using String init(format:_:) initializer

Swift’s String type is bridged with Foundation’s NSString class (you can learn more about it by reading The Swift Programming Language). Therefore, you can use the following code in order to return a String from your rounding operation:

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. Using NumberFormatter

If you expect to get a String? from your rounding operation, NumberFormatter offers a highly customizable solution.

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


回答5:

In Swift 2.0 and Xcode 7.2:

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

Example:

\"enter



回答6:

Building on Yogi\'s answer, here\'s a Swift function that does the job:

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


回答7:

This is fully worked code

Swift 3.0/4.0 , Xcode 9.0 GM/9.2

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

output - 123

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

output - 123.3

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

output - 123.33

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

output - 123.326



回答8:

The code for specific digits after decimals is:

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

Here the %.3f tells the swift to make this number rounded to 3 decimal places.and if you want double number, you may use this code:

// String to Double

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



回答9:

In Swift 3.0 and Xcode 8.0:

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

Use this extension like this,

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


回答10:

A handy way can be the use of extension of type Double

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

Usage:

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


回答11:

Use the built in Foundation Darwin library

SWIFT 3

extension Double {

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

}

Usage:

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

Outputs: 12.988



回答12:

This is a sort of a long workaround, which may come in handy if your needs are a little more complex. You can use a number formatter in Swift.

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

Suppose your variable you want to print is

var printVar = 3.567

This will make sure it is returned in the desired format:

numberFormatter.StringFromNumber(printVar)

The result here will thus be \"3.6\" (rounded). While this is not the most economic solution, I give it because the OP mentioned printing (in which case a String is not undesirable), and because this class allows for multiple parameters to be set.



回答13:

I would use

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

and change .3f to any number of decimal numbers you need



回答14:

Not Swift but I\'m sure you get the idea.

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


回答15:

This is more flexible algorithm of rounding to N significant digits

Swift 3 solution

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


回答16:

The best way to format a double property is to use the Apple predefined methods.

mutating func round(_ rule: FloatingPointRoundingRule)

FloatingPointRoundingRule is a enum which has following possibilities

Enumeration Cases:

case awayFromZero Round to the closest allowed value whose magnitude is greater than or equal to that of the source.

case down Round to the closest allowed value that is less than or equal to the source.

case toNearestOrAwayFromZero Round to the closest allowed value; if two values are equally close, the one with greater magnitude is chosen.

case toNearestOrEven Round to the closest allowed value; if two values are equally close, the even one is chosen.

case towardZero Round to the closest allowed value whose magnitude is less than or equal to that of the source.

case up Round to the closest allowed value that is greater than or equal to the source.

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


回答17:

round a double value to x number of decimal
NO. of digits after decimal

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


回答18:

If you want to round Double values, you might want to use Swift Decimal so you don\'t introduce any errors that can crop up when trying to math with these rounded values. If you use Decimal, it can accurately represent decimal values of that rounded floating point value.

So you can do:

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

Then, you can get the rounded Decimal value like so:

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

And if you want to display it with a specified number of decimal places (as well as localize the string for the user\'s current locale), you can use a NumberFormatter:

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

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


回答19:

Either:

  1. Using String(format:):

    • Typecast Double to String with %.3f format specifier and then back to Double

      Double(String(format: \"%.3f\", 10.123546789))!
      
    • Or extend Double to handle N-Decimal places:

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

    • multiply with 10^3, round it and then divide by 10^3...

      (1000 * 10.123546789).rounded()/1000
      
    • Or extend Double to handle N-Decimal places:

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


回答20:

I found this wondering if it is possible to correct a user\'s input. That is if they enter three decimals instead of two for a dollar amount. Say 1.111 instead of 1.11 can you fix it by rounding? The answer for many reasons is no! With money anything over i.e. 0.001 would eventually cause problems in a real checkbook.

Here is a function to check the users input for too many values after the period. But which will allow 1., 1.1 and 1.11.

It is assumed that the value has already been checked for successful conversion from a String to a 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


}


回答21:

//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\"