Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.python > #66694 > unrolled thread
| Started by | "Mark H. Harris" <harrismh777@gmail.com> |
|---|---|
| First post | 2014-02-19 07:30 -0800 |
| Last post | 2014-02-27 02:37 -0800 |
| Articles | 20 on this page of 41 — 11 participants |
Back to article view | Back to comp.lang.python
extend methods of decimal module "Mark H. Harris" <harrismh777@gmail.com> - 2014-02-19 07:30 -0800
Re: extend methods of decimal module Terry Reedy <tjreedy@udel.edu> - 2014-02-19 13:59 -0500
Re: extend methods of decimal module "Mark H. Harris" <harrismh777@gmail.com> - 2014-02-19 13:30 -0800
Re: extend methods of decimal module Zachary Ware <zachary.ware+pylist@gmail.com> - 2014-02-19 15:54 -0600
Re: extend methods of decimal module Terry Reedy <tjreedy@udel.edu> - 2014-02-19 17:10 -0500
Re: extend methods of decimal module "Mark H. Harris" <harrismh777@gmail.com> - 2014-02-27 04:07 -0800
Re: extend methods of decimal module Oscar Benjamin <oscar.j.benjamin@gmail.com> - 2014-02-27 14:42 +0000
Re: extend methods of decimal module "Mark H. Harris" <harrismh777@gmail.com> - 2014-02-27 07:42 -0800
Re: extend methods of decimal module Chris Angelico <rosuav@gmail.com> - 2014-02-28 02:57 +1100
Re: extend methods of decimal module Oscar Benjamin <oscar.j.benjamin@gmail.com> - 2014-02-27 16:24 +0000
Re: extend methods of decimal module "Mark H. Harris" <harrismh777@gmail.com> - 2014-02-27 15:00 -0800
Re: extend methods of decimal module Wolfgang <xpysol@gmail.com> - 2014-02-27 15:43 -0800
Re: extend methods of decimal module Oscar Benjamin <oscar.j.benjamin@gmail.com> - 2014-02-27 23:50 +0000
Re: extend methods of decimal module "Mark H. Harris" <harrismh777@gmail.com> - 2014-02-27 18:15 -0800
Re: extend methods of decimal module Chris Angelico <rosuav@gmail.com> - 2014-02-28 15:26 +1100
Re: extend methods of decimal module "Mark H. Harris" <harrismh777@gmail.com> - 2014-02-27 21:18 -0800
Re: extend methods of decimal module Chris Angelico <rosuav@gmail.com> - 2014-02-28 16:26 +1100
Re: extend methods of decimal module Wolfgang Maier <wolfgang.maier@biologie.uni-freiburg.de> - 2014-02-28 08:54 +0000
Re: extend methods of decimal module "Mark H. Harris" <harrismh777@gmail.com> - 2014-02-28 10:23 -0800
Re: extend methods of decimal module Steven D'Aprano <steve@pearwood.info> - 2014-02-28 03:15 +0000
Re: extend methods of decimal module "Mark H. Harris" <harrismh777@gmail.com> - 2014-02-27 20:41 -0800
Re: extend methods of decimal module Chris Angelico <rosuav@gmail.com> - 2014-02-28 16:00 +1100
Re: extend methods of decimal module Steven D'Aprano <steve@pearwood.info> - 2014-02-28 07:34 +0000
Re: extend methods of decimal module Chris Angelico <rosuav@gmail.com> - 2014-02-28 19:52 +1100
Re: extend methods of decimal module Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2014-02-28 15:11 +0000
Re: extend methods of decimal module Chris Angelico <rosuav@gmail.com> - 2014-03-01 02:36 +1100
Re: extend methods of decimal module "Mark H. Harris" <harrismh777@gmail.com> - 2014-02-28 10:34 -0800
Re: extend methods of decimal module Chris Angelico <rosuav@gmail.com> - 2014-03-01 05:37 +1100
Re: extend methods of decimal module "Mark H. Harris" <harrismh777@gmail.com> - 2014-02-28 11:26 -0800
Re: extend methods of decimal module "Mark H. Harris" <harrismh777@gmail.com> - 2014-02-28 11:39 -0800
Re: extend methods of decimal module "Mark H. Harris" <harrismh777@gmail.com> - 2014-02-28 12:17 -0800
Re: extend methods of decimal module Terry Reedy <tjreedy@udel.edu> - 2014-02-27 12:07 -0500
Re: extend methods of decimal module Anssi Saari <as@sci.fi> - 2014-03-01 08:55 +0200
Re: extend methods of decimal module "Mark H. Harris" <harrismh777@gmail.com> - 2014-03-01 16:49 -0800
Re: extend methods of decimal module Chris Angelico <rosuav@gmail.com> - 2014-02-28 04:48 +1100
Re: extend methods of decimal module Zachary Ware <zachary.ware+pylist@gmail.com> - 2014-02-19 16:27 -0600
Re: extend methods of decimal module casevh@gmail.com - 2014-02-19 21:11 -0800
Re: extend methods of decimal module "Mark H. Harris" <harrismh777@gmail.com> - 2014-02-27 02:33 -0800
Re: extend methods of decimal module casevh@gmail.com - 2014-02-28 06:23 -0800
Re: extend methods of decimal module Oscar Benjamin <oscar.j.benjamin@gmail.com> - 2014-02-19 22:29 +0000
Re: extend methods of decimal module "Mark H. Harris" <harrismh777@gmail.com> - 2014-02-27 02:37 -0800
Page 1 of 3 [1] 2 3 Next page →
| From | "Mark H. Harris" <harrismh777@gmail.com> |
|---|---|
| Date | 2014-02-19 07:30 -0800 |
| Subject | extend methods of decimal module |
| Message-ID | <02cdd9c7-aef7-4cc7-a813-cd1c9627ceb4@googlegroups.com> |
Would it be possible to extend the methods of the decimal module just a bit to include atan(), sin(), cos(), and exp() ? The module has methods for ln() and sqrt(); and that's great! I have done some rudimentary searching of the pep history and I'm not finding any pep related to extending the decimal module with other scientific functions. It is easy to write them in pure python, of course, but I am interested in having the same performance boost with atan(), sin(), cos(), and exp() as I see with the rest of the decimal module on 3.3/ Is it possible anytime sooner than later? By-the-by, my hat is off to the person(s) who did the 3.3 work on the decimal module --- the performance boost is astounding. My agm routine for pi100k (which runs on 3.2.3 2Ghz in 10.5 minutes) runs on the same processor in 42 seconds on 3.3.4/ very nice.
[toc] | [next] | [standalone]
| From | Terry Reedy <tjreedy@udel.edu> |
|---|---|
| Date | 2014-02-19 13:59 -0500 |
| Message-ID | <mailman.7160.1392836388.18130.python-list@python.org> |
| In reply to | #66694 |
On 2/19/2014 10:30 AM, Mark H. Harris wrote: > Would it be possible to extend the methods of the decimal module just > a bit to include atan(), sin(), cos(), and exp() ? The decimal module implements IEEE 854 > The module has methods for ln() and sqrt(); and that's great! that includes just these. I am not sure if we have an ironclad policy against adding things not in the standard. > By-the-by, my hat is off to the person(s) who did the 3.3 work on the > decimal module --- the performance boost is astounding. Stephen (?) Krah. -- Terry Jan Reedy
[toc] | [prev] | [next] | [standalone]
| From | "Mark H. Harris" <harrismh777@gmail.com> |
|---|---|
| Date | 2014-02-19 13:30 -0800 |
| Message-ID | <94b1962a-0004-4c5b-b484-972a166b88b5@googlegroups.com> |
| In reply to | #66711 |
> > The decimal module implements IEEE 854 > Thanks Terry... ... long time. I would like to find out if there is some iron-clad policy about extending the implementation of an IEEE standard... decimal module in this case; I'm just thinking that this particular extension really fits the python "batteries included" philosophy. I guess what I'm really asking for are the same routines found in "bc -l" math library. I've finally moved my number crunching stuff to python (from bc) because the performance of "decimal" is finally way better than bc for the moment, and wrapping python are the math routines for control and processing is so much better. Anyway, sure would be nice to have a very speedy atan() function built-in for decimal. Thanks for the answer... I hope you enjoy the day!
[toc] | [prev] | [next] | [standalone]
| From | Zachary Ware <zachary.ware+pylist@gmail.com> |
|---|---|
| Date | 2014-02-19 15:54 -0600 |
| Message-ID | <mailman.7164.1392846870.18130.python-list@python.org> |
| In reply to | #66718 |
On Wed, Feb 19, 2014 at 3:30 PM, Mark H. Harris <harrismh777@gmail.com> wrote: >> >> The decimal module implements IEEE 854 >> > > Thanks Terry... ... long time. > > I would like to find out if there is some iron-clad policy about extending > the implementation of an IEEE standard... decimal module in this case; I'm > just thinking that this particular extension really fits the python > "batteries included" philosophy. > > I guess what I'm really asking for are the same routines found in "bc -l" > math library. I've finally moved my number crunching stuff to python (from bc) > because the performance of "decimal" is finally way better than bc for the > moment, and wrapping python are the math routines for control and processing > is so much better. Anyway, sure would be nice to have a very speedy atan() > function built-in for decimal. You might consider suggesting a "decimal.math" module on python-ideas. -- Zach
[toc] | [prev] | [next] | [standalone]
| From | Terry Reedy <tjreedy@udel.edu> |
|---|---|
| Date | 2014-02-19 17:10 -0500 |
| Message-ID | <mailman.7165.1392847869.18130.python-list@python.org> |
| In reply to | #66718 |
On 2/19/2014 4:54 PM, Zachary Ware wrote: > On Wed, Feb 19, 2014 at 3:30 PM, Mark H. Harris <harrismh777@gmail.com> wrote: >>> >>> The decimal module implements IEEE 854 >>> >> >> Thanks Terry... ... long time. >> >> I would like to find out if there is some iron-clad policy about extending >> the implementation of an IEEE standard... decimal module in this case; I'm >> just thinking that this particular extension really fits the python >> "batteries included" philosophy. >> >> I guess what I'm really asking for are the same routines found in "bc -l" >> math library. I've finally moved my number crunching stuff to python (from bc) >> because the performance of "decimal" is finally way better than bc for the >> moment, and wrapping python are the math routines for control and processing >> is so much better. Anyway, sure would be nice to have a very speedy atan() >> function built-in for decimal. > > You might consider suggesting a "decimal.math" module on python-ideas. Or just dmath. I think this is a better idea than suggesting additions to decimal itself. For one thing, anything put in decimal would be subject to change if the function were to be added to the standard. It is worth noting in such a posting that Krah's speedup make such functions really feasible. The algorithms could be similar, at least initially, to the one used for floats. -- Terry Jan Reedy
[toc] | [prev] | [next] | [standalone]
| From | "Mark H. Harris" <harrismh777@gmail.com> |
|---|---|
| Date | 2014-02-27 04:07 -0800 |
| Message-ID | <55525f2c-fd3a-4927-b642-2dbf5eae7e9b@googlegroups.com> |
| In reply to | #66721 |
On Wednesday, February 19, 2014 4:10:22 PM UTC-6, Terry Reedy wrote: > Or just dmath. I think this is a better idea than suggesting additions > to decimal itself. For one thing, anything put in decimal would be > subject to change if the function were to be added to the standard. It > is worth noting in such a posting that Krah's speedup make such > functions really feasible. The algorithms could be similar, at least > initially, to the one used for floats > > Terry Jan Reedy I have created a project here: https://code.google.com/p/pythondecimallibrary/ I wrote a dmath.py library module for use with the C accelerated decimal module, that I would like to see merged into the C Python distribution so that folks have it by default... without having to pull it down with GIT, or whatever. Anyway, thanks for responding (everyone) and for your consideration. Oh, and one more thing... whoever is doing the work on IDLE these days, nice job! It is stable, reliable, and just works/ appreciate it! Kind regards, Mark H Harris
[toc] | [prev] | [next] | [standalone]
| From | Oscar Benjamin <oscar.j.benjamin@gmail.com> |
|---|---|
| Date | 2014-02-27 14:42 +0000 |
| Message-ID | <mailman.7422.1393512211.18130.python-list@python.org> |
| In reply to | #67139 |
On 27 February 2014 12:07, Mark H. Harris <harrismh777@gmail.com> wrote:
>
> I have created a project here:
>
> https://code.google.com/p/pythondecimallibrary/
>
> I wrote a dmath.py library module for use with the C accelerated decimal module, that I would like to see merged into the C Python distribution so that folks have it by default... without having to pull it down with GIT, or whatever.
Hi Mark,
Some points:
1) Why have you committed the code as a .tar.gz file?
2) This function is not such a good idea:
def D(numform):
return Decimal(str(numform))
The Decimal constructor already accepts strings and many types of
numbers. Going via str like this reduces accuracy if e.g. someone
passes a float in.
3) In many places you've written code like this:
prec=dscale(getcontext().prec +7)
sqr = (D(x).sqrt()).__round__(prec)
retscale=dscale(prec)
The preferred way is:
with localcontext() as ctx:
ctx.prec += 7
sqr = round(D(x).sqrt(), prec)
i.e. use a context manager to restore the context even if an error
occurs and use the round builtin rather than the dunder method.
4) You shouldn't be using round/__round__ for precision rounding: it
uses decimal places rather than significant figures. If you want to
round to context precision just use unary +. i.e.:
return +sqr
5) The Decimal.sqrt method already rounds to context precision.
There's no need to compute in higher precision and then round it
yourself (in fact you're invalidating the correctness of the rounding
by double-rounding like this). So really it's just:
def sqrt(x):
return Decimal(x).sqrt()
6) You should organise it in such a way that you're not progressively
increasing the precision each time one function calls another.
Oscar
[toc] | [prev] | [next] | [standalone]
| From | "Mark H. Harris" <harrismh777@gmail.com> |
|---|---|
| Date | 2014-02-27 07:42 -0800 |
| Message-ID | <360e87d2-4daf-4222-8ebe-51f3e4d1fade@googlegroups.com> |
| In reply to | #67143 |
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...
> 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..?
> 3) In many places you've written code like this:
>
> prec=dscale(getcontext().prec +7)
> sqr = (D(x).sqrt()).__round__(prec)
> retscale=dscale(prec)
> The preferred way is:
> with localcontext() as ctx:
> ctx.prec += 7
> sqr = round(D(x).sqrt(), prec)
Thanks...
> i.e. use a context manager to restore the context even if an error
> occurs and use the round builtin rather than the dunder method.
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__(...) ??
> 4) You shouldn't be using round/__round__ for precision rounding: it
> uses decimal places rather than significant figures. If you want to
> round to context precision just use unary +. i.e.:
> return +sqr
whoohoo! thank you.
> 5) The Decimal.sqrt method already rounds to context precision.
> There's no need to compute in higher precision and then round it
> yourself (in fact you're invalidating the correctness of the rounding
> by double-rounding like this). So really it's just:
> def sqrt(x):
> return Decimal(x).sqrt()
ok...
>
> 6) You should organise it in such a way that you're not progressively
> increasing the precision each time one function calls another.
right... well, and if you look at the calls to my __atan__ funcs I am assuming that they are still in the context of the calling func atan().... right...? so no need to even have the bump up in prec there.
>
> Oscar
thanks Oscar, I really appreciate your comments
marcus
[toc] | [prev] | [next] | [standalone]
| From | Chris Angelico <rosuav@gmail.com> |
|---|---|
| Date | 2014-02-28 02:57 +1100 |
| Message-ID | <mailman.7426.1393516682.18130.python-list@python.org> |
| In reply to | #67148 |
On Fri, Feb 28, 2014 at 2:42 AM, Mark H. Harris <harrismh777@gmail.com> wrote: >> 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... When you commit changes, though, it has to treat it as a completely changed binary file - utterly opaque, and it destroys your space savings too. Normal source code diffs are tiny, and they're readable. Check out the history of this file; you can easily see every change I've made: https://github.com/Rosuav/ExceptExpr/commits/master/find_except_expr.py Click on any commit to see what happened there. It's hard to do that with .tar.gz blobs. ChrisA
[toc] | [prev] | [next] | [standalone]
| From | Oscar Benjamin <oscar.j.benjamin@gmail.com> |
|---|---|
| Date | 2014-02-27 16:24 +0000 |
| Message-ID | <mailman.7429.1393518292.18130.python-list@python.org> |
| In reply to | #67148 |
On 27 February 2014 15:42, Mark H. Harris <harrismh777@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
[toc] | [prev] | [next] | [standalone]
| From | "Mark H. Harris" <harrismh777@gmail.com> |
|---|---|
| Date | 2014-02-27 15:00 -0800 |
| Message-ID | <9f7b535f-9e5d-45df-96f6-6cd8f6b4a524@googlegroups.com> |
| In reply to | #67155 |
On Thursday, February 27, 2014 10:24:23 AM UTC-6, Oscar Benjamin wrote:
>>>> from decimal import Decimal as D
> >>> D(0.1)
> Decimal('0.1000000000000000055511151231257827021181583404541015625')
>
hi Oscar, well, that's not what I'm doing with my D()... I'm not just making D() mimic Decimal... look inside it... there's a str() call.... consider the following experiment and you'll see what I'm talking about...
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').
Ok, so for the experiment: consider this exchange from IDLE:
>>> ==================== RESTART ==
>>> from dmath import *
>>> pi = D(piagm(32))
>>> pi
Decimal('3.14159265358979323846264338327950')
>>> pi * Decimal(.1)
Decimal('0.31415926535897934128560682837111')
>>>
>>> pi * D(.1)
Decimal('0.31415926535897932384626433832795')
>>>
>>>
You will notice that Decimal(.1) and my D(.1) work against PI differently... in fact, Decimal(.1) decimates PI.... <sorry for the PUN>
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)
Now, try this experiment: again from IDLE and dmath.py
>>> ===================== RESTART =
>>> from dmath import *
>>> pi = D(piagm(32))
>>> pi
Decimal('3.14159265358979323846264338327950')
>>>
>>> pi * Decimal(str(.1))
Decimal('0.31415926535897932384626433832795')
>>>
>>> pi * D(.1)
Decimal('0.31415926535897932384626433832795')
>>>
You see? All of my functions make certain that when the Decimal objects are created that the string constructor gets called.... so that, in this case, .1 really is 0.1 precisely, not floating format.
and take a look at this:
>>> ================ RESTART ==
>>> from dmath import *
>>> D(.1)
Decimal('0.1')
>>>
With my dmath D() function the object holds 0.1 precisely... its not the float 0.1 rounded ...
shifting gears a bit....
The real reason I pushed a commit of a gzip tarball to code.google is because I've never used code.google before (thought I'd give it a try) and I didn't realize that its only pull / download is a ZIP... so I am going to take your advice and remove the tarballs and just put the code up there... if I can get my GIT to work ... its GIVing me fits (probably because I don't have it configured right on this system.
Thanks for the discussion... its helping me get this stuff into my brain, and its giving me a chance to discuss what's at issue between floats and Decimal.
kind regards,
marcus
[toc] | [prev] | [next] | [standalone]
| From | Wolfgang <xpysol@gmail.com> |
|---|---|
| Date | 2014-02-27 15:43 -0800 |
| Message-ID | <fda2f422-2534-40ef-8afc-d3dae3ca2ed4@googlegroups.com> |
| In reply to | #67175 |
On Friday, February 28, 2014 12:00:45 AM UTC+1, Mark H. Harris wrote:
> On Thursday, February 27, 2014 10:24:23 AM UTC-6, Oscar Benjamin wrote:
> >>>> from decimal import Decimal as D
>
> > >>> D(0.1)
>
> > Decimal('0.1000000000000000055511151231257827021181583404541015625')
>
> >
> hi Oscar, well, that's not what I'm doing with my D()... I'm not just making D() mimic Decimal... look inside it... there's a str() call.... consider the following experiment and you'll see what I'm talking about...
> >>> from dmath import *
> >>> pi = D(piagm(32))
> >>> pi
> Decimal('3.14159265358979323846264338327950')
> >>> pi * Decimal(.1)
> Decimal('0.31415926535897934128560682837111')
> >>>
> >>> pi * D(.1)
> Decimal('0.31415926535897932384626433832795')
> >>>
> >>>
> You will notice that Decimal(.1) and my D(.1) work against PI differently... in fact, Decimal(.1) decimates PI.... <sorry for the PUN>
> 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)
What Oscar tried to make clear is that Decimal(.1) does not store an "erroneous" float in the Decimal object, but an "honest" one.
Maybe this becomes clearer with calculations instead of literal values:
>>> 1000000000000000055511151231257827021181583404541015625/10**55
0.1
now try:
D(1000000000000000055511151231257827021181583404541015625/10**55)
vs
Decimal(1000000000000000055511151231257827021181583404541015625/10**55)
for an example that it is not better, in general, to go through the str()-mediated rounding you are doing.
Of course, if the user *intended* that float(.1) to mean exactly 0.1 then your D class does a good job at guessing, but only then.
The Decimal class, on the other hand, leaves the choice to the user as you are illustrating perfectly in your own example below:
> Now, try this experiment: again from IDLE and dmath.py
>
> >>> from dmath import *
> >>> pi = D(piagm(32))
> >>> pi
> Decimal('3.14159265358979323846264338327950')
> >>>
> >>> pi * Decimal(str(.1))
> Decimal('0.31415926535897932384626433832795')
> >>>
> >>> pi * D(.1)
> Decimal('0.31415926535897932384626433832795')
see? Decimal(.1) gives the exact representation of the float, but Decimal('.1') is the way to get your D()'s default behavior.
> You see? All of my functions make certain that when the Decimal objects are created that the string constructor gets called.... so that, in this case, .1 really is 0.1 precisely, not floating format.
Yes, just that this is almost never what your users will want to happen ...
The Decimal constructor is really well designed, so stick to it !
Best,
Wolfgang
[toc] | [prev] | [next] | [standalone]
| From | Oscar Benjamin <oscar.j.benjamin@gmail.com> |
|---|---|
| Date | 2014-02-27 23:50 +0000 |
| Message-ID | <mailman.7448.1393545085.18130.python-list@python.org> |
| In reply to | #67175 |
On 27 February 2014 23:00, Mark H. Harris <harrismh777@gmail.com> wrote:
> On Thursday, February 27, 2014 10:24:23 AM UTC-6, Oscar Benjamin wrote:
>
>>>>> from decimal import Decimal as D
>> >>> D(0.1)
>> Decimal('0.1000000000000000055511151231257827021181583404541015625')
>
> hi Oscar, well, that's not what I'm doing with my D()... I'm not just making D() mimic Decimal... look inside it... there's a str() call.... consider the following experiment and you'll see what I'm talking about...
I understood what your code is doing but I'm not sure if you do.
Calling str on a float performs an inexact binary to decimal
conversion. Calling Decimal on a float performs an exact binary to
decimal conversion. Your reasoning essentially assumes that every
float should be interpreted as an approximate representation for a
nearby decimal value. This is probably true if the user wrote "a =
0.1" but is generally not true in the kind of numeric code that is
likely to be using the transcendental functions defined in your dmath
module.
Calling Decimal(str(float)) introduces entirely avoidable inaccuracy
in your code when the primary purpose of your code as accuracy!
Oscar
[toc] | [prev] | [next] | [standalone]
| From | "Mark H. Harris" <harrismh777@gmail.com> |
|---|---|
| Date | 2014-02-27 18:15 -0800 |
| Message-ID | <63868b32-5206-435d-aa3d-f3040f65f72b@googlegroups.com> |
| In reply to | #67179 |
On Thursday, February 27, 2014 5:50:55 PM UTC-6, Oscar Benjamin wrote:
> . . . Calling Decimal on a float performs an exact binary to
> decimal conversion. Your reasoning essentially assumes that every
> float should be interpreted as an approximate representation for a
> nearby decimal value.
That is the whole point exactly. Yes, the exact binary to decimal conversion for float is the problem precisely. But my solution does not assume any such thing... because the decimal module is "advertised" to support what I'm doing. In other words, decimal correctly builds from a string literal in any case; even intermediary values. Well, the digits past 16 (for a double) aren't valid anyway... and the ones before that (when passed to decimal as a string) get correctly created as a decimal object.
But here is the other point... I am not planning on passing *any* of these functions a float... my system that uses dmath uses strings only, or decimals. str(Decimal) works, as does Decimal(str()). So, I'm not really interested in floats at all... but, and here's the big BUT, I'm expecting folks to use dmath.py from the console (as I plan to) and they are going to punch in *floats*. why? because its natural.
Its just easier to type D(2.78) than Deciaml('2.78').
Neither here nor there.... but the frustration is the fact that floats are so 1982. Know what I mean? This is the 21st century, and you know what, we have got to get past this:
>>>
>>> Decimal(.1)
Decimal('0.1000000000000000055511151231257827021181583404541015625')
>>>
In other words, we need to move to a numeric system of computation (decimal is a good start) that does not keep track of real values as IEEE floats. Back in the day, that was ok... today, its unacceptable.
So, Krah has a really nice (and fast) module that will help at least the python community (and users) to move into the 21st century. My hat is off to Stefan, that's for sure.
I am going to keep playing with this, and making the changes you have suggested... I'll put the code back up there on code.google and see if I can make the repository work like its supposed to..
Thanks again for your assistance, I really appreciate it, Oscar.
[toc] | [prev] | [next] | [standalone]
| From | Chris Angelico <rosuav@gmail.com> |
|---|---|
| Date | 2014-02-28 15:26 +1100 |
| Message-ID | <mailman.7452.1393561622.18130.python-list@python.org> |
| In reply to | #67185 |
On Fri, Feb 28, 2014 at 1:15 PM, Mark H. Harris <harrismh777@gmail.com> wrote:
> Its just easier to type D(2.78) than Deciaml('2.78').
It's easier to type 2.78 than 2.718281828, too, but one of them is
just plain wrong. Would you tolerate using 2.78 for e because it's
easier to type? I mean, it's gonna be close.
Create Decimal values from strings, not from the str() of a float,
which first rounds in binary and then rounds in decimal.
ChrisA
[toc] | [prev] | [next] | [standalone]
| From | "Mark H. Harris" <harrismh777@gmail.com> |
|---|---|
| Date | 2014-02-27 21:18 -0800 |
| Message-ID | <aa444b5e-5814-45ec-9d55-612c30e0a1d7@googlegroups.com> |
| In reply to | #67191 |
On Thursday, February 27, 2014 10:26:59 PM UTC-6, Chris Angelico wrote: > Create Decimal values from strings, not from the str() of a float, > which first rounds in binary and then rounds in decimal. > Thanks Chris... another excellent point... ok, you guys have about convinced me (which is spooky) but, hey, I'm teachable... what is the best strategy then? Many of the functions of my dmath.py are algorithms which calculate infinite series to convergence out there at some number of precision ... do I make the assumption that all functions will take a string as argument and then let interactive users bare the responsibility to enter a string or decimal... avoiding floats... or use try and toss and error if the rules are not followed, or what? I do see that I'm trying to solve a problem the wrong way,... just not sure what is the better approach. If you get a chance, take a look at the dmath.py code on: https://code.google.com/p/pythondecimallibrary/ I got the repository working correctly for me, and the files can be viewed on-line ... its a long script, but not that hard to look through because all the functions pretty much work the same... when you've seen one converging series function in python you've seen them all! Thanks again, Chris.
[toc] | [prev] | [next] | [standalone]
| From | Chris Angelico <rosuav@gmail.com> |
|---|---|
| Date | 2014-02-28 16:26 +1100 |
| Message-ID | <mailman.7457.1393565180.18130.python-list@python.org> |
| In reply to | #67198 |
On Fri, Feb 28, 2014 at 4:18 PM, Mark H. Harris <harrismh777@gmail.com> wrote: > do I make the assumption that all functions will take a string as argument and then let interactive users bare the responsibility to enter a string or decimal... avoiding floats... Just have your users pass in Decimal objects. They can construct them however they wish. ChrisA
[toc] | [prev] | [next] | [standalone]
| From | Wolfgang Maier <wolfgang.maier@biologie.uni-freiburg.de> |
|---|---|
| Date | 2014-02-28 08:54 +0000 |
| Message-ID | <mailman.7462.1393577681.18130.python-list@python.org> |
| In reply to | #67198 |
Mark H. Harris <harrismh777 <at> gmail.com> writes: > > On Thursday, February 27, 2014 10:26:59 PM UTC-6, Chris Angelico wrote: > > > Create Decimal values from strings, not from the str() of a float, > > which first rounds in binary and then rounds in decimal. > > > > Thanks Chris... another excellent point... ok, you guys have about convinced me (which is spooky) but, > hey, I'm teachable... what is the best strategy then? > > If you get a chance, take a look at the dmath.py code on: > > https://code.google.com/p/pythondecimallibrary/ > > I got the repository working correctly for me, and the files can be viewed on-line ... its a long script, but > not that hard to look through because all the functions pretty much work the same... when you've seen one > converging series function in python you've seen them all! > > Thanks again, Chris. > Hi Mark, I quickly skimmed through your code and I don't think there is a need for your D() function at all. I thought it was a class adding some extra functionality to Decimal (that's because you used a capital letter for its name), but now I realized that it's just a function returning a Decimal from the string representation of its argument. Since by now, I guess, we all agree that using the string representation is the wrong approach, you can simply use Decimal instead of D() throughout your code. Best, Wolfgang
[toc] | [prev] | [next] | [standalone]
| From | "Mark H. Harris" <harrismh777@gmail.com> |
|---|---|
| Date | 2014-02-28 10:23 -0800 |
| Message-ID | <40c397ad-079a-4086-b6f6-52abb1d5d9de@googlegroups.com> |
| In reply to | #67209 |
On Friday, February 28, 2014 2:54:12 AM UTC-6, Wolfgang Maier wrote: > Since by now, I guess, we all agree that using the string representation is > the wrong approach, you can simply use Decimal instead of D() throughout > your code. > Best, > Wolfgang hi Wolfgang, ...right... I'm going to clean it up. Thanks, 'preciate it.
[toc] | [prev] | [next] | [standalone]
| From | Steven D'Aprano <steve@pearwood.info> |
|---|---|
| Date | 2014-02-28 03:15 +0000 |
| Message-ID | <530fff58$0$11113$c3e8da3@news.astraweb.com> |
| In reply to | #67175 |
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
[toc] | [prev] | [next] | [standalone]
Page 1 of 3 [1] 2 3 Next page →
Back to top | Article view | comp.lang.python
csiph-web