On Thu, Mar 05, 2020 at 12:39:38PM +0000, Steve Barnes wrote:
Hmm, is there a PEP regarding Decimal literals?
No.
I couldn't find one, although there is PEP 240 regarding rational literals. Maybe it's time to write up a rejected PEP explaining exactly what the problems are with Decimal literals.
The proposal hasn't been rejected, it just faded away for lack of somebody to write the PEP and offer to do the work. As I recall, a number of senior core developers were tentatively interested in the idea, at least in principle. Nick Coghlan was one, if memory serves me right. I don't recall any major objections, although that part might be confirmation bias :-)
From memory, the problems are (a) it'd effectively require the gigantic decimal module to be imported by default, and (b) contexts don't work with literals.
Neither of those are problems. They are only problems if you expect the (hypothetical) builtin decimal type to be the exact decimal.Decimal type, but that is overkill for the use-cases for a builtin decimals. All the context related functionality would be dropped: builtin.decimal would implement only a fixed width with a single rounding mode. They would be effectively like float, only base 10. For those who need the extra functionality of decimal.Decimal, the module would still exist. But builtins.decimal would be aimed at the simpler use-case of numerically unsophisticated users who wouldn't know a rounding mode or trap if it bit them but do know that 0.1 + 0.2 should equal 0.3 :-) There are a couple of standards for fixed-width decimals, by memory we were considering either 64 bit or 128 bit decimals.
I think that a part of the problem is that because Decimal silently accepts both float and string literal inputs things get surprising e.g.:
In [5]: D(0.3) == D('0.3') Out[5]: False
That's only surprising to those who don't read the docs :-)
1. Deprecate the use of the Decimal initialiser with float inputs so that decimal.Decimal(0.1), etc., issues a warning [...] 3. Eventually outlaw float as an input to the initialiser.
Please no. We started there, and relaxed that restriction because it was more annoying than helpful. Going backwards to Python 2.5 or thereabouts is, well, going backwards: >>> decimal.Decimal(0.5) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/local/lib/python2.5/decimal.py", line 648, in __new__ "First convert the float to a string") TypeError: Cannot convert float to Decimal. First convert the float to a string Numerically unsophisticated users are not the only users of Decimal, and frankly, the unsophisticated users aren't going to be any less surprised by Decimal(0.1) raising an exception than they are surprised by any of the other floating-point oddities that affect both decimal and float.
While this would "break" code which happen to work as expected, e.g. decimal.Decimal(0.5) but at least we would have consistent behaviour and fail early rather than potentially working but giving incorrect or inconsistent results.
It's not giving incorrect results. It is giving correct results. The problem is not Decimal, but that people think that 0.1 means one tenth when it actually means 3602879701896397 รท 36028797018963968 :-) Viewing Decimal(0.1) is an excellent way to discover what value 0.1 actually has, as opposed to the value you think it has. -- Steven