Edit: this is in python 2.7.7
Has anyone come across this? I need to serialise an key-value pair object into a string, in which each key contains a float.
There can be any number of entries (I demonstrate just one, "Test", in this example).
It seems that when evaluating the whole object, Python can add decimals to the value, whereas when we look at those exact same values independantly (outside of the object), they appear "normal".
I need a way to make the final, serialised object contain the original values (0.08 in this case) ; or a means to force them to 2DPs.
userConstraints = {
"test": 0.08
}
print userConstraints["test"] # Appears ok here
print str(userConstraints) # Has extra DPs here. Why? How to fix?
Output:
0.8
{'test': 0.080000000000000002}
Serialisation logic (on the other side)
obj=ast.literal_eval(theSerialisedString)
Output of serialised obj
{'test': 0.080000000000000002}
So, all in all, your version and implementation of Python has a very precise repr()
for floating-point numbers. (We can tell it's not a vanilla CPython 2.7.7, since the shorter floating-point repr()
from Python 3.1 has been backported into CPython 2.7.0, and that version doesn't do reprs like you say it does.)
Your wire format for serialization is Python repr()
, and you want to round off some of those lengthy floats, because reasons.
That's no problem, since Python comes with reprlib
- a module for customizing repr
(confusingly repr
in Python 2).
Et voilà:
import repr as reprlib # Python 2
import reprlib # Python 3
class TruncatedFloatRepr(reprlib.Repr):
def repr_float(self, x, level):
v = repr(x)
if "." in v:
decimals_after_dot = len(v.partition(".")[-1])
if decimals_after_dot > 5: # Too many decimals? Truncate!
return "%.2f" % x
# Otherwise, use the vanilla `repr`.
return v
userConstraints = {
"test": 0.08000000004,
"hello": ["world", "beep", 3.0, "boop"],
"one": ("two", 1.1),
}
print(TruncatedFloatRepr().repr(userConstraints))
The output is
{'hello': ['world', 'beep', 3.0, 'boop'], 'one': ('two', 1.1), 'test': 0.08}
and of course you might want to consider changing that 5
to something else, or maybe count the number of zeroes in the decimal part, or whatever suits your business logic fancy. As it is, this implementation will just always truncate numbers with too much decimal precision, and that's likely not what you really want.