extend methods of decimal module

Oscar Benjamin oscar.j.benjamin at gmail.com
Thu Feb 27 17:24:23 CET 2014


On 27 February 2014 15:42, Mark H. Harris <harrismh777 at gmail.com> wrote:
> On Thursday, February 27, 2014 8:42:55 AM UTC-6, Oscar Benjamin wrote:
>
>>
>> Some points:
>
>    Thanks so much... you have clarified some things I was struggling with...
>
>> 1) Why have you committed the code as a .tar.gz file?
>
>     um, to save space... well, I know its tiny, but its just a habit I have... 5kb instead of 25kb...

It made it awkward for me to see the code you were referring to. In a
separate thread you asked about how to share code samples. These days
it's common practice to host code somewhere it can be viewed from a
browser. This means that I can do things like link to a line in Chris'
code:
https://github.com/Rosuav/ExceptExpr/blob/master/examples.py#L169

There are many other reasons (as mentioned by Chris) why committing a
binary archive instead of just the plain old files is a generally bad
idea with standard version control systems. A relevant one is that
every change you make to your code, even if you only change 2 lines
will require an additional 5kb of space to store the new .tar.gz file
alongside the old rather than 200 bytes or whatever it is to store the
diff of the code files.

>> 2) This function is not such a good idea:
>>
>> def D(numform):
>>     return Decimal(str(numform))
>
>     The reason for this is not for strings, but for float literals.  All of my functions may take a float literal and things still work, because the D(float) function converts the float literal to a string, which is then passed to the Decimal constructor.    so... this works  sqrt(0.1)  or,  sqrt(2.01)   Without the D() function float literals may not be passed to the Decimal because it really cannot handle them...  0.1  and others cause it a fit...    is there another way to do what I'm looking for here..?

I think you're misunderstanding what's going on:

>>> from decimal import Decimal as D
>>> D(0.1)
Decimal('0.1000000000000000055511151231257827021181583404541015625')

There is no binary floating point value that is exactly equal to the
mathematical number 0.1. So when you write 0.1 in Python the number is
rounded to the nearest binary floating point number. When you create a
Decimal from a float it will create a Decimal with the *exact* same
value that the float had. That's exactly what you're seeing above.

When you print a float normally it pretends to have the value 0.1 but
that's really a lie:
>>> 0.1  # There is no float with the true value 0.1!!!!!
0.1
>>> 0.1 == D('0.1')
False

When you convert a float to a string it does not show the exact value
of the float but rather a rounded, shortened decimal form. By
converting float->str->Decimal you are introducing unnecessary
rounding. The Decimal constructor deliberately never rounds its inputs
and every float can be represented as a Decimal with a finite number
of digits. So Decimal(float) is exact but your D function is not.

It is precisely the fact that binary floating point cannot represent
seemingly simple non-integer decimal numbers like 0.1 that leads to
the creation of the decimal module. But why would anyone pass a float
into one of your dmath functions since they're surely much slower than
the math module? The main reason that I can imagine is that they would
like a really accurate result in which case rounding all floats on
input is unacceptable.

>      This has confused me, because when I look into the module help text I don't see roun() all I see is
>     __round__(...)     how do I know when the   round()  method exists vs.  __round__(...)  ??

The round builtin function calls __round__ on any object. Dunder
methods are not supposed to be called directly.


Oscar



More information about the Python-list mailing list