Home > Enterprise >  How to properly reduce scale and format a Float value in Swift?
How to properly reduce scale and format a Float value in Swift?

Time:11-02

I'm trying to properly reduce scale, formatting a float value and returning it as a String in Swift.

For example:

let value: Float = 4.8962965

// formattedFalue should be 4.90 or 4,90 based on localization
let formattedValue = value.formatNumber()

Here is what I did:

extension Float {

func reduceScale(to places: Int) -> Float {
      let multiplier = pow(10, Float(places))
      let newDecimal = multiplier * self // move the decimal right
      let truncated = Float(Int(newDecimal)) // drop the fraction
      let originalDecimal = truncated / multiplier // move the decimal back                 return originalDecimal
 }

func formatNumber() -> String {
      let num = abs(self)

      let numberFormatter = NumberFormatter()
      numberFormatter.usesGroupingSeparator = true
      numberFormatter.minimumFractionDigits = 0
      numberFormatter.maximumFractionDigits = 2
      numberFormatter.roundingMode = .up
      numberFormatter.numberStyle = .decimal
      numberFormatter.locale = // we take it from app settings 

      let formatted = num.reduceScale(to: 2)
      let returningString = numberFormatter.string(from: NSNumber(value: formatted))!
      return "\(returningString)"

}

}

But when I use this code I get 4.89 (or 4,89 depending on the localization) instead of 4.90 (or 4,90) as I expect.

Thanks in advance.

CodePudding user response:

Solved as suggested by Sulthan by removing reduceScale method and put minimumFractionDigits equal to 2.

CodePudding user response:

You get 4.89 because reduceScale(to:) turns the number into 4.89 (actually, probably 4.89000something because 4.89 cannot be expressed exactly as a binary floating point). When the number formatter truncates this to two decimal places, it naturally rounds it down.

In fact, you don't need reduceScale(to:) at all because the rounding function of the number formatter will do it for you.

Also the final string interpolation is unnecessary because the result of NumberFormatter.string(from:) is automatically bridged to a String?

Also (see comments below by Dávid Pásztor and Sulthan) you can use string(for:) to obviate the NSNumber conversion.

This is what you need

import Foundation

extension Float {

    func formatNumber() -> String {
          let num = abs(self)

          let numberFormatter = NumberFormatter()
          numberFormatter.usesGroupingSeparator = true
          numberFormatter.minimumFractionDigits = 0
          numberFormatter.maximumFractionDigits = 2
          numberFormatter.roundingMode = .up
          numberFormatter.numberStyle = .decimal
          numberFormatter.locale = whatever
          return numberFormatter.string(for: num)!
    }
}

let value: Float = 4.8962965

// formattedFalue should be 4.90 or 4,90 based on localization
let formattedValue = value.formatNumber() // "4.9"
  • Related