On Thu, 27 Feb 2014 15:00:45 -0800, Mark H. Harris wrote: > Decimal does not keep 0.1 as a floating point format (regardless of > size) which is why banking can use Decimal without having to worry about > the floating formatting issue... in other words, 0.0 is not stored in > Decimal as any kind of floating value... its not rounded.... it really > is Decimal('0.1').
I'm sorry, but that is incorrect. Decimal is a floating point format, same as float. Decimal uses base 10, so it is a better fit for numbers we write out in base 10 like "0.12345", but otherwise it suffers from the same sort of floating point rounding issues as floats do. py> a = Decimal("1.1e20") py> b = Decimal("1.1e-20") py> assert b != 0 py> a + b == a True In the case of 0.1 (I assume your "0.0" above was a typo), it is a floating point value. You can inspect the fields' values like this: py> x = Decimal("0.1") py> x.as_tuple() DecimalTuple(sign=0, digits=(1,), exponent=-1) There's a sequence of digits, and an exponent that tells you where the decimal point goes. That's practically the definition of "floating point". In Python 3.2 and older, you can even see those fields as non- public attributes: py> x._int '1' py> x._exp -1 (In Python 3.3, the C implementation does not allow access to those attributes from Python.) This is perhaps a better illustrated with a couple of other examples: py> Decimal('1.2345').as_tuple() DecimalTuple(sign=0, digits=(1, 2, 3, 4, 5), exponent=-4) py> Decimal('1234.5').as_tuple() DecimalTuple(sign=0, digits=(1, 2, 3, 4, 5), exponent=-1) [...] > The reason is that Decimal(.1) stores the erroneous float in the Decimal > object including the float error for .1 and D(.1) works correctly > because the D(.1) function in my dmath.py first converts the .1 to a > string value before handing it to Decimal's constructor(s) That *assumes* that when the user types 0.1 as a float value, they actually intend it to have the value of 1/10 rather than the exact value of 3602879701896397/36028797018963968. That's probably a safe bet, with a number like 0.1, typed as a literal. But how about this number? py> x = 3832879701896397/36028797218963967 py> Decimal(x) Decimal('0.10638378180104603176747701809290447272360324859619140625') py> Decimal(str(x)) Decimal('0.10638378180104603') Are you *absolutely* sure that the user intended x to have the second value rather than the first? How do you know? In other words, what you are doing is automatically truncating calculated floats at whatever string display format Python happens to use, regardless of the actual precision of the calculation. That happens to work okay with some values that the user types in by hand, like 0.1. But it is a disaster for *calculated* values. Unfortunately, there is no way for your D function to say "only call str on the argument if it is a floating point literal typed by the user". But what you can do is follow the lead of the decimal module, and leave the decision up to the user. The only safe way to avoid *guessing* what value the caller wanted is to leave the choice of truncating floats up to them. That's what the decimal module does, and it is the right decision. If the user passes a float directly, they should get *exact conversion*, because you have no way of knowing whether they actually wanted the float to be truncated or not. If they do want to truncate, they can pass it to string themselves, or supply a string literal. -- Steven -- https://mail.python.org/mailman/listinfo/python-list