Округление двойного значения до x количества знаков после запятой в быстром

Может кто-нибудь сказать мне, как округлить двойное значение до х числа десятичных знаков в Swift?

Я имею:

var totalWorkTimeInHours = (totalWorkTime/60/60)

С totalWorkTime будучи NSTimeInterval (двойной) в секунду.

totalWorkTimeInHours даст мне часы, но это даст мне количество времени в таком длинном точном числе, например 1.543240952039......

Как мне округлить это, скажем, до 1.543, когда я печатаю totalWorkTimeInHours?

37 ответов

Решение

Вы можете использовать Swift's round Функция для достижения этой цели.

Округлить Double с точностью до 3 цифр сначала умножьте его на 1000, округлите и разделите округленный результат на 1000:

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

Кроме любого вида printf(...) или же String(format: ...) решения, результат этой операции по-прежнему имеет тип Double,

РЕДАКТИРОВАТЬ:
Что касается комментариев, которые иногда не работают, пожалуйста, прочитайте это:

Что каждый ученый должен знать об арифметике с плавающей точкой

Удлинитель для 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

В Свифт 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, округленный до 4 десятичных знаков:

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

Использовать String конструктор, который принимает format строка:

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

С Swift 4, в соответствии с вашими потребностями, вы можете выбрать один из 9 следующих стилей, чтобы получить округленный результат от Double,


#1. С помощью FloatingPointrounded() метод

В простейшем случае вы можете использовать Doubleround() метод.

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(_:) метод

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. Использование Дарвина round функция

Фонд предлагает 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. Используя Double пользовательский метод расширения, созданный с помощью Darwin round а также pow функции

Если вы хотите повторить предыдущую операцию много раз, рефакторинг вашего кода может быть хорошей идеей.

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:_:) инициализатор

Свифта String тип соединен с Фондом NSString класс (вы можете узнать больше об этом, читая язык программирования Swift). Таким образом, вы можете использовать следующий код для возврата 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")

В Swift 2.0 и Xcode 7.2:

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

Пример:

Это полностью проработанный код

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)

выход - 123

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

выход - 123,3

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

выход - 123,33

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

выход - 123,326

Основываясь на ответе Йога, вот функция Swift, которая делает эту работу:

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

В 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

Swift 4, Xcode 10

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

Код для конкретных цифр после десятичных знаков:

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

Здесь%.3f сообщает swift, чтобы это число было округлено до 3 десятичных разрядов. И если вы хотите двойное число, вы можете использовать этот код:

// String to Double

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

Используйте встроенную библиотеку 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

Если вы хотите округлить 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)
}

Удобным способом может быть использование расширения типа 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

Это своего рода долгий обходной путь, который может пригодиться, если ваши потребности немного сложнее. Вы можете использовать средство форматирования чисел в 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. С помощью String(format:):

    • напечатанный материал Double в String с %.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
          }
      }
      

округлить двойное значение до x десятичного числа
NO. цифр после десятичной

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

Для удобства использования я создал расширение:

      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

Спасибо за вдохновение других ответов!

Это более гибкий алгоритм округления до 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

Я хотел бы использовать

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

и измените.3f на любое количество десятичных чисел, которое вам нужно

Swift 5

используя метод String

      var yourDouble = 3.12345
//to round this to 2 decimal spaces i could turn it into string
let roundingString = String(format: "%.2f", myDouble)
let roundedDouble = Double(roundingString) //and than back to double
// result is 3.12 

но более приемлемо использовать расширение

      extension Double {
    func round(to decimalPlaces: Int) -> Double {
        let precisionNumber = pow(10,Double(decimalPlaces))
        var n = self // self is a current value of the Double that you will round
        n = n * precisionNumber
        n.round()
        n = n / precisionNumber
        return n
    }
}

а затем вы можете использовать:

      yourDouble.round(to:2)

Лучший способ отформатировать двойное свойство - это использовать предопределенные методы Apple.

mutating func round(_ rule: FloatingPointRoundingRule)

FloatingPointRoundingRule - это перечисление, которое имеет следующие возможности

Случаи перечисления:

case awayFromZero Округлить до ближайшего допустимого значения, величина которого больше или равна величине источника.

case down Округление до ближайшего допустимого значения, которое меньше или равно источнику.

case toNearestOrAwayFromZero Округлить до ближайшего допустимого значения; если два значения одинаково близки, выбирается значение с большей величиной.

case toNearestOrEven Округлить до ближайшего допустимого значения; если два значения одинаково близки, выбирается четное.

регистр в направлении нуля. Округление до ближайшего допустимого значения, величина которого меньше или равна величине источника.

case up Округлить до ближайшего допустимого значения, которое больше или равно источнику.

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

Решение сработало для меня. XCode 13.3.1 и Swift 5

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

Тест:

      print(-87.7183123123.rounded(decimalPoint: 3))
print(-87.7188123123.rounded(decimalPoint: 3))
print(-87.7128123123.rounded(decimalPoint: 3))

Результат:

      -87.718
-87.719
-87.713

Не Свифт, но я уверен, что вы поняли идею.

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

Кажется, это работает в Swift 5.

Очень удивлен, что для этого уже нет стандартной функции.

// Усечение двойного числа до n десятичных знаков с округлением

extension Double {

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

}

В iOS 15 / macOS 12 были представлены новые встроенные средства форматирования чисел.

Без какого-либо правила округления оно округляется, как учили в школе . Синтаксис

      let value = 1.543240
let rounded = value.formatted(.number.precision(.fractionLength(2))))

округлить до 1,54 и

      let value = 1.545240
let rounded = value.formatted(.number.precision(.fractionLength(2))))

округляет до 1,55,fractionLengthуказывает количество дробных цифр

Чтобы принудительно округлить в меньшую сторону, добавьте правило округления.

      let rounded = value.formatted(.number.rounded(rule: .down).precision(.fractionLength(2))))

Чтобы избежать недостатков Float, используйте Decimal

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 округляет до 1075,57 при использовании Float со шкалой: 2 и вниз
1075,58 округляет до 1075,58 при использовании Decimal со шкалой: 2 и вниз

var n = 123.111222333
n = Double(Int(n * 10.0)) / 10.0

Результат: n = 123,1

Измените 10.0 (1 десятичный знак) на любое из 100.0 (2 десятичных знака), 1000.0 (3 десятичных знака) и так далее для количества цифр после десятичного разделителя.

Я обнаружил, что это интересно, если можно исправить ввод пользователя. Это если они вводят три знака после запятой вместо двух для суммы в долларах. Скажем, 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


}

Вот один для SwiftUI, если вам нужен текстовый элемент с числовым значением.

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

    var body : some View {
        Text(String(format: "%.\(digits)f", number))
    }
}

Вы можете добавить это расширение:

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
Другие вопросы по тегам