Python float to Decimal conversion

PythonDecimal

Python Problem Overview


Python Decimal doesn't support being constructed from float; it expects that you have to convert float to a string first.

This is very inconvenient since standard string formatters for float require that you specify number of decimal places rather than significant places. So if you have a number that could have as many as 15 decimal places you need to format as Decimal("%.15f" % my_float), which will give you garbage at the 15th decimal place if you also have any significant digits before decimal (Decimal("%.15f" % 100000.3) == Decimal('100000.300000000002910')).

Can someone suggest a good way to convert from float to Decimal preserving value as the user has entered, perhaps limiting number of significant digits that can be supported?

Python Solutions


Solution 1 - Python

Python <2.7
"%.15g" % f

Or in Python 3.0:

format(f, ".15g")
Python 2.7+, 3.2+

Just pass the float to Decimal constructor directly, like this:

from decimal import Decimal
Decimal(f)

Solution 2 - Python

You said in your question:

> Can someone suggest a good way to > convert from float to Decimal > preserving value as the user has > entered

But every time the user enters a value, it is entered as a string, not as a float. You are converting it to a float somewhere. Convert it to a Decimal directly instead and no precision will be lost.

Solution 3 - Python

I suggest this

>>> a = 2.111111
>>> a
2.1111110000000002
>>> str(a)
'2.111111'
>>> decimal.Decimal(str(a))
Decimal('2.111111')

Solution 4 - Python

Python does support Decimal creation from a float. You just cast it as a string first. But the precision loss doesn't occur with string conversion. The float you are converting doesn't have that kind of precision in the first place. (Otherwise you wouldn't need Decimal)

I think the confusion here is that we can create float literals in decimal format, but as soon as the interpreter consumes that literal the inner representation becomes a floating point number.

Solution 5 - Python

The "official" string representation of a float is given by the repr() built-in:

>>> repr(1.5)
'1.5'
>>> repr(12345.678901234567890123456789)
'12345.678901234567'
You can use repr() instead of a formatted string, the result won't contain any unnecessary garbage.

Solution 6 - Python

you can convert and than quantize to keep 5 digits after comma via

Decimal(float).quantize(Decimal("1.00000"))

Solution 7 - Python

When you say "preserving value as the user has entered", why not just store the user-entered value as a string, and pass that to the Decimal constructor?

Solution 8 - Python

The main answer is slightly misleading. The g format ignores any leading zeroes after the decimal point, so format(0.012345, ".2g") returns 0.012 - three decimal places. If you need a hard limit on the number of decimal places, use the f formatter: format(0.012345, ".2f") == 0.01

Solution 9 - Python

The "right" way to do this was documented in 1990 by Steele and White's and Clinger's PLDI 1990 papers.

You might also look at this SO discussion about Python Decimal, including my suggestion to try using something like frap to rationalize a float.

Solution 10 - Python

You can use JSON to accomplish it

import json
from decimal import Decimal

float_value = 123456.2365
decimal_value = json.loads(json.dumps(float_value), parse_float=Decimal)

Solution 11 - Python

Inspired by this answer I found a workaround that allows to shorten the construction of a Decimal from a float bypassing (only apparently) the string step:

import decimal
class DecimalBuilder(float):
    def __or__(self, a):
         return decimal.Decimal(str(a))

>>> d = DecimalBuilder()
>>> x = d|0.1
>>> y = d|0.2
>>> x + y # works as desired
Decimal('0.3')
>>> d|0.1 + d|0.2 # does not work as desired, needs parenthesis
TypeError: unsupported operand type(s) for |: 'decimal.Decimal' and 'float'
>>> (d|0.1) + (d|0.2) # works as desired
Decimal('0.3')

It's a workaround but it surely allows savings in code typing and it's very readable.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionKozyarchukView Question on Stackoverflow
Solution 1 - PythonjfsView Answer on Stackoverflow
Solution 2 - PythonnoskloView Answer on Stackoverflow
Solution 3 - Pythonvincent wenView Answer on Stackoverflow
Solution 4 - PythonmuhukView Answer on Stackoverflow
Solution 5 - PythonFederico A. RamponiView Answer on Stackoverflow
Solution 6 - PythonRyabchenko AlexanderView Answer on Stackoverflow
Solution 7 - PythonPaul FisherView Answer on Stackoverflow
Solution 8 - PythonChrisView Answer on Stackoverflow
Solution 9 - PythonDoug CurrieView Answer on Stackoverflow
Solution 10 - PythonDeep PatelView Answer on Stackoverflow
Solution 11 - PythonmmjView Answer on Stackoverflow