Question

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.

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?

Was it helpful?

Solution

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)

OTHER TIPS

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.

I suggest this

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

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.

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.

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?

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top