0

I found a strange behavior in Swift's NumberFormatter.

func testNumberFormat() {
    let numberFormat = { (number: NSNumber) -> (String?) in
        let formatter: NumberFormatter = NumberFormatter()
        formatter.numberStyle = .decimal
        formatter.maximumFractionDigits = 2
        formatter.minimumFractionDigits = 2
        formatter.roundingMode = .halfUp

        return formatter.string(from: number)
    }

    XCTAssertEqual(numberFormat(Decimal(0.004) as NSNumber)!, "0.00") // OK
    XCTAssertEqual(numberFormat(Decimal(0.005) as NSNumber)!, "0.01") // OK
    XCTAssertEqual(numberFormat(Decimal(10000.004) as NSNumber)!, "10,000.00") // OK
    XCTAssertEqual(numberFormat(Decimal(10000.005) as NSNumber)!, "10,000.01") // NG!
    XCTAssertEqual(numberFormat(Decimal(10000.014) as NSNumber)!, "10,000.01") // OK
    XCTAssertEqual(numberFormat(Decimal(10000.015) as NSNumber)!, "10,000.02") // OK
}

The following test is failed.

XCTAssertEqual(numberFormat(Decimal(10000.005) as NSNumber)!, "10,000.01") 

I expect "10,000, 01", but "10,000, 00" was return. Is my understanding mistaken?

rmaddy
  • 314,917
  • 42
  • 532
  • 579
mgeng
  • 1
  • 1
  • If you use `Double` it works fine which makes me wonder about some rounding issues with `Decimal` type probably causing that issue. – manman Oct 05 '18 at 03:18
  • `print(Decimal(10000.005) as NSNumber)` prints `10000.004999999997952`, so that would round to `10000.00`. – vacawama Oct 05 '18 at 03:20
  • 4
    See [is floating point math broken](https://stackoverflow.com/q/588004/1630618) – vacawama Oct 05 '18 at 03:22

1 Answers1

1

Try the following code:

print(Decimal(10000.005)) //->10000.004999999997952

You know 10000.004999999997952 will be formatted as 10,000.00 with your numberFormat.

Binary floating point Double cannot represent the precise value of 10000.005, so the result of Decimal.init(_: Double) is hard to predict.

If you want to hold a precise decimal representation in Decimal, you should use Decimal(string:) instead.

print(Decimal(string: "10000.005")!) //->10000.005
print(numberFormat(Decimal(string: "10000.005")! as NSNumber)!) //->10,000.01

If you want to convert Double to Decimal with precisely representing a printed value, you may need some more code.

OOPer
  • 47,149
  • 6
  • 107
  • 142