# Round float to x decimals?

I feel compelled to provide a counterpoint to Ashwini Chaudhary's answer. Despite appearances, the two-argument form of the `round`

function *does not* round a Python float to a given number of decimal places, and it's often not the solution you want, even when you think it is. Let me explain...

The ability to round a (Python) float to some number of decimal places is something that's frequently requested, but turns out to be rarely what's actually needed. The beguilingly simple answer `round(x, number_of_places)`

is something of an attractive nuisance: it *looks* as though it does what you want, but thanks to the fact that Python floats are stored internally in binary, it's doing something rather subtler. Consider the following example:

```
>>> round(52.15, 1)
52.1
```

With a naive understanding of what `round`

does, this looks wrong: surely it should be rounding *up* to `52.2`

rather than *down* to `52.1`

? To understand why such behaviours can't be relied upon, you need to appreciate that while this looks like a simple decimal-to-decimal operation, it's far from simple.

So here's what's *really* happening in the example above. (*deep breath*) We're displaying a *decimal* representation of the nearest *binary* floating-point number to the nearest `n`

-digits-after-the-point *decimal* number to a *binary* floating-point approximation of a numeric literal written in *decimal*. So to get from the original numeric literal to the displayed output, the underlying machinery has made *four* separate conversions between binary and decimal formats, two in each direction. Breaking it down (and with the usual disclaimers about assuming IEEE 754 binary64 format, round-ties-to-even rounding, and IEEE 754 rules):

First the numeric literal

`52.15`

gets parsed and converted to a Python float. The actual number stored is`7339460017730355 * 2**-47`

, or`52.14999999999999857891452847979962825775146484375`

.Internally as the first step of the

`round`

operation, Python computes the closest 1-digit-after-the-point decimal string to the stored number. Since that stored number is a touch under the original value of`52.15`

, we end up rounding down and getting a string`52.1`

. This explains why we're getting`52.1`

as the final output instead of`52.2`

.Then in the second step of the

`round`

operation, Python turns that string back into a float, getting the closest binary floating-point number to`52.1`

, which is now`7332423143312589 * 2**-47`

, or`52.10000000000000142108547152020037174224853515625`

.Finally, as part of Python's read-eval-print loop (REPL), the floating-point value is displayed (in decimal). That involves converting the binary value back to a decimal string, getting

`52.1`

as the final output.

In Python 2.7 and later, we have the pleasant situation that the two conversions in step 3 and 4 cancel each other out. That's due to Python's choice of `repr`

implementation, which produces the shortest decimal value guaranteed to round correctly to the actual float. One consequence of that choice is that if you start with any (not too large, not too small) decimal literal with 15 or fewer significant digits then the corresponding float will be displayed showing those exact same digits:

```
>>> x = 15.34509809234
>>> x
15.34509809234
```

Unfortunately, this furthers the illusion that Python is storing values in decimal. Not so in Python 2.6, though! Here's the original example executed in Python 2.6:

```
>>> round(52.15, 1)
52.200000000000003
```

Not only do we round in the opposite direction, getting `52.2`

instead of `52.1`

, but the displayed value doesn't even print as `52.2`

! This behaviour has caused numerous reports to the Python bug tracker along the lines of "round is broken!". But it's not `round`

that's broken, it's user expectations. (Okay, okay, `round`

is a *little* bit broken in Python 2.6, in that it doesn't use correct rounding.)

Short version: if you're using two-argument round, and you're expecting predictable behaviour from a *binary* approximation to a *decimal* round of a *binary* approximation to a *decimal* halfway case, you're asking for trouble.

So enough with the "two-argument round is bad" argument. What *should* you be using instead? There are a few possibilities, depending on what you're trying to do.

If you're rounding for display purposes, then you don't want a float result at all; you want a string. In that case the answer is to use string formatting:

`>>> format(66.66666666666, '.4f') '66.6667' >>> format(1.29578293, '.6f') '1.295783'`

Even then, one has to be aware of the internal binary representation in order not to be surprised by the behaviour of apparent decimal halfway cases.

`>>> format(52.15, '.1f') '52.1'`

If you're operating in a context where it matters which direction decimal halfway cases are rounded (for example, in some financial contexts), you might want to represent your numbers using the

`Decimal`

type. Doing a decimal round on the`Decimal`

type makes a lot more sense than on a binary type (equally, rounding to a fixed number of binary places makes perfect sense on a binary type). Moreover, the`decimal`

module gives you better control of the rounding mode. In Python 3,`round`

does the job directly. In Python 2, you need the`quantize`

method.`>>> Decimal('66.66666666666').quantize(Decimal('1e-4')) Decimal('66.6667') >>> Decimal('1.29578293').quantize(Decimal('1e-6')) Decimal('1.295783')`

In rare cases, the two-argument version of

`round`

really*is*what you want: perhaps you're binning floats into bins of size`0.01`

, and you don't particularly care which way border cases go. However, these cases are rare, and it's difficult to justify the existence of the two-argument version of the`round`

builtin based on those cases alone.

Use the built-in function `round()`

:

```
In [23]: round(66.66666666666,4)
Out[23]: 66.6667
In [24]: round(1.29578293,6)
Out[24]: 1.295783
```

help on `round()`

:

round(number[, ndigits]) -> floating point number

Round a number to a given precision in decimal digits (default 0 digits). This always returns a floating point number. Precision may be negative.