I am using String(format:)
to convert a Float
. I thought the number would be rounded.
Sometimes it is.
String(format: "%.02f", 1.455) //"1.46"
Sometimes not.
String(format: "%.02f", 1.555) //"1.55"
String(round(1.555 * 100) / 100.0) //"1.56"
I guess 1.55 cannot be represented exactly as binary. And that it becomes something like 1.549999XXXX
But NumberFormatter
doesn't seem to cause the same problem... Why? Should it be preferred over String(format:)
?
let formatter = NumberFormatter()
formatter.maximumFractionDigits = 2
formatter.minimumFractionDigits = 2
if let string = formatter.string(for: 1.555) {
print(string) // 1.56
}
Reference to the problem (to use
String (format :)
to round a decimal number) can be found in the answers (or more often comments) to these questions: Rounding a double value to x number of decimal places in swift and How to format a Double into Currency - Swift 3. But the problem it covers (math with FloatingPoint) has been dealt with many times on SO (for all languages).String(format:)
does not have the function of rounding a decimal number (even if it is unfortunately proposed in some answers) but of formatting it (as its name suggests). This formatting sometimes causes a rounding. That is true. But we have to keep in mind a problem that the number 1.555 is... not worth 1.555.In Swift,
Double
andFloat
, that conform to theFloatingPoint
protocol respect the IEEE 754 specification. However, some values cannot be exactly represented by the IEEE 754 standard.To be convinced of this, we can use The Float Converter to convert between the decimal representation of numbers (like "1.02") and the binary format used by all modern CPUs (IEEE 754 floating point). For
1.555
, the value actually stored in float is1.55499994754791259765625
So the problem does not come from
String (format :)
. For example, we can try another way to round to the thousandth and we find the same problem. :That is how it is : "Binary floating point arithmetic is fine so long as you know what's going on and don't expect values to be exactly the decimal ones you put in your program".
So the real question is : is this really a problem for you to use ? It depends on the app. Generally if we convert a number into a String by limiting its precision (by rounding), it is because we consider that this precision is not useful to the user. If this is the kind of data we're talking about, then it's okay to use a
FloatingPoint
.However, to format it it may be more relevant to use a
NumberFormatter
. Not necessarily for its rounding algorithm, but rather because it allows you to locate the format :Conversely, if we are in a case where precision matters, we must abandon
Double
/Float
and useDecimal
. Still to keep our rounding example, we can use this extension (which may be the best answer to the question "Rounding a double value to x number of decimal places in swift ") :