Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Python > Floating point subtraction rounding error (NOT display error)

Reply
Thread Tools

Floating point subtraction rounding error (NOT display error)

 
 
Keflavich
Guest
Posts: n/a
 
      12-13-2007
Hey, I have a bit of code that died on a domain error when doing an
arcsin, and apparently it's because floating point subtraction is
having problems. I know about the impossibility of storing floating
point numbers precisely, but I was under the impression that the
standard used for that last digit would prevent subtraction errors
from compounding.

Is there a simple solution to this problem, or do I need to run some
sort of check at every subtraction to make sure that my float does not
deviate? I'm not sure I know even how to do that.

A sample of the failure:
ipdb>1.0000000000000001 == 1
True
ipdb>R
0.69999999999999996
ipdb>R==.7
True
ipdb>y2
3.2999999999999998
ipdb>y2 == 3.3
True
ipdb>cirY-y2
0.70000000000000018
ipdb>cirY-y2 == .7
False

I was unable to find solutions when searching the web because all of
the hits I got were discussing display issues, which I'm not concerned
with.

Thanks,
Adam
 
Reply With Quote
 
 
 
 
Aahz
Guest
Posts: n/a
 
      12-13-2007
In article <(E-Mail Removed)>,
Keflavich <(E-Mail Removed)> wrote:
>
>Hey, I have a bit of code that died on a domain error when doing an
>arcsin, and apparently it's because floating point subtraction is
>having problems. I know about the impossibility of storing floating
>point numbers precisely, but I was under the impression that the
>standard used for that last digit would prevent subtraction errors
>from compounding.
>
>Is there a simple solution to this problem, or do I need to run some
>sort of check at every subtraction to make sure that my float does not
>deviate? I'm not sure I know even how to do that.


Switch to Decimal module? Available in 2.4 and later.
--
Aahz ((E-Mail Removed)) <*> http://www.pythoncraft.com/

"Typing is cheap. Thinking is expensive." --Roy Smith
 
Reply With Quote
 
 
 
 
Keflavich
Guest
Posts: n/a
 
      12-13-2007
Thanks, I'll have a look at that. I'm not sure the decimal type is
included in numpy, though, which is what I'm using. It doesn't show
up in their documentation, at least.

Adam

On Dec 13, 3:39 pm, (E-Mail Removed) (Aahz) wrote:
> In article <(E-Mail Removed)>,
>
> Keflavich <(E-Mail Removed)> wrote:
>
> >Hey, I have a bit of code that died on a domain error when doing an
> >arcsin, and apparently it's because floating point subtraction is
> >having problems. I know about the impossibility of storing floating
> >point numbers precisely, but I was under the impression that the
> >standard used for that last digit would prevent subtraction errors
> >from compounding.

>
> >Is there a simple solution to this problem, or do I need to run some
> >sort of check at every subtraction to make sure that my float does not
> >deviate? I'm not sure I know even how to do that.

>
> Switch to Decimal module? Available in 2.4 and later.
> --
> Aahz ((E-Mail Removed)) <*> http://www.pythoncraft.com/
>
> "Typing is cheap. Thinking is expensive." --Roy Smith


 
Reply With Quote
 
Keflavich
Guest
Posts: n/a
 
      12-13-2007
The decimal package isn't what I'm looking for - I don't want to have
to retype every variable in my code, and I have arcsines showing up on
about a dozen lines right now. It also seems like a rather
complicated way to deal with the problem; maybe I just need to
implement my own rounding code, but I figured something of that sort
must already exist.

Thanks though,
Adam


On Dec 13, 3:50 pm, Keflavich <(E-Mail Removed)> wrote:
> Thanks, I'll have a look at that. I'm not sure the decimal type is
> included in numpy, though, which is what I'm using. It doesn't show
> up in their documentation, at least.
>
> Adam
>
> On Dec 13, 3:39 pm, (E-Mail Removed) (Aahz) wrote:
>
> > In article <(E-Mail Removed)>,

>
> > Keflavich <(E-Mail Removed)> wrote:

>
> > >Hey, I have a bit of code that died on a domain error when doing an
> > >arcsin, and apparently it's because floating point subtraction is
> > >having problems. I know about the impossibility of storing floating
> > >point numbers precisely, but I was under the impression that the
> > >standard used for that last digit would prevent subtraction errors
> > >from compounding.

>
> > >Is there a simple solution to this problem, or do I need to run some
> > >sort of check at every subtraction to make sure that my float does not
> > >deviate? I'm not sure I know even how to do that.

>
> > Switch to Decimal module? Available in 2.4 and later.
> > --
> > Aahz ((E-Mail Removed)) <*> http://www.pythoncraft.com/

>
> > "Typing is cheap. Thinking is expensive." --Roy Smith


 
Reply With Quote
 
Keflavich
Guest
Posts: n/a
 
      12-13-2007
Solved: used round(number,12) in this case for all of the operands of
my arcsines. Not pretty, but at least VIM made it easy...

Thanks for the help,
Adam


On Dec 13, 4:01 pm, Keflavich <(E-Mail Removed)> wrote:
> The decimal package isn't what I'm looking for - I don't want to have
> to retype every variable in my code, and I have arcsines showing up on
> about a dozen lines right now. It also seems like a rather
> complicated way to deal with the problem; maybe I just need to
> implement my own rounding code, but I figured something of that sort
> must already exist.
>
> Thanks though,
> Adam
>
> On Dec 13, 3:50 pm, Keflavich <(E-Mail Removed)> wrote:
>
> > Thanks, I'll have a look at that. I'm not sure the decimal type is
> > included in numpy, though, which is what I'm using. It doesn't show
> > up in their documentation, at least.

>
> > Adam

>
> > On Dec 13, 3:39 pm, (E-Mail Removed) (Aahz) wrote:

>
> > > In article <(E-Mail Removed)>,

>
> > > Keflavich <(E-Mail Removed)> wrote:

>
> > > >Hey, I have a bit of code that died on a domain error when doing an
> > > >arcsin, and apparently it's because floating point subtraction is
> > > >having problems. I know about the impossibility of storing floating
> > > >point numbers precisely, but I was under the impression that the
> > > >standard used for that last digit would prevent subtraction errors
> > > >from compounding.

>
> > > >Is there a simple solution to this problem, or do I need to run some
> > > >sort of check at every subtraction to make sure that my float does not
> > > >deviate? I'm not sure I know even how to do that.

>
> > > Switch to Decimal module? Available in 2.4 and later.
> > > --
> > > Aahz ((E-Mail Removed)) <*> http://www.pythoncraft.com/

>
> > > "Typing is cheap. Thinking is expensive." --Roy Smith


 
Reply With Quote
 
Steven D'Aprano
Guest
Posts: n/a
 
      12-14-2007
On Thu, 13 Dec 2007 14:30:18 -0800, Keflavich wrote:

> Hey, I have a bit of code that died on a domain error when doing an
> arcsin, and apparently it's because floating point subtraction is having
> problems.


I'm not convinced that your diagnosis is correct. Unless you're using
some weird, uncommon hardware, it's unlikely that a bug in the floating
point subtraction routines has escaped detection. (Unlikely, but not
impossible.) Can you tell us what values give you incorrect results?



> I know about the impossibility of storing floating point
> numbers precisely, but I was under the impression that the standard used
> for that last digit would prevent subtraction errors from compounding.


What gave you that impression? Are you referring to guard digits?


> Is there a simple solution to this problem, or do I need to run some
> sort of check at every subtraction to make sure that my float does not
> deviate? I'm not sure I know even how to do that.


I still don't quite understand your problem. If you think your float is
deviating from the correct value, that implies you know what the correct
value should be. How do you know what the correct value should be?

I should also mention that of course your answer will deviate, due to the
finite precision of floats.


> A sample of the failure:
> ipdb>1.0000000000000001 == 1
> True
> ipdb>R
> 0.69999999999999996
> ipdb>R==.7
> True
> ipdb>y2
> 3.2999999999999998
> ipdb>y2 == 3.3
> True
> ipdb>cirY-y2
> 0.70000000000000018


What's cirY? How do you know this is the incorrect value?

> ipdb>cirY-y2 == .7
> False


Obviously not. As you've already shown, the correct value is
0.70000000000000018, not 0.69999999999999996 (the closest floating point
value to 0.7).


> I was unable to find solutions when searching the web because all of the
> hits I got were discussing display issues, which I'm not concerned with.


Have you read this?
http://docs.sun.com/source/806-3568/ncg_goldberg.html




--
Steven.

 
Reply With Quote
 
Keflavich
Guest
Posts: n/a
 
      12-14-2007
On Dec 13, 5:52 pm, Steven D'Aprano <st...@REMOVE-THIS-
cybersource.com.au> wrote:
> On Thu, 13 Dec 2007 14:30:18 -0800, Keflavich wrote:
> > Hey, I have a bit of code that died on a domain error when doing an
> > arcsin, and apparently it's because floating point subtraction is having
> > problems.

>
> I'm not convinced that your diagnosis is correct. Unless you're using
> some weird, uncommon hardware, it's unlikely that a bug in the floating
> point subtraction routines has escaped detection. (Unlikely, but not
> impossible.) Can you tell us what values give you incorrect results?


Here's a better (more complete) example [btw, I'm using ipython]:

In [39]: x = 3.1 + .6
In [40]: y = x - 3.1
In [41]: y == 6
Out[41]: False
In [42]: x == 3.7
Out[42]: True
In [43]: 3.1+.6-3.1 == .6
Out[43]: False
In [45]: (3.1+.6-3.1)/.6
Out[45]: 1.0000000000000002
In [46]: (3.1+.6-3.1)/.6 == 1
Out[46]: False
In [47]: (3.1+.6-3.1)/.6 > 1
Out[47]: True

Therefore, if I try to take the arcsine of that value, instead of
being arcsine(1) = pi, I have arcsine(1.(16 zeroes)1) = math domain
error. However, see below, I now understand the error.

> > I know about the impossibility of storing floating point
> > numbers precisely, but I was under the impression that the standard used
> > for that last digit would prevent subtraction errors from compounding.

>
> What gave you that impression? Are you referring to guard digits?


I believe that's what I was referring to; I have only skimmed the
topic, haven't gone into a lot of depth

> > Is there a simple solution to this problem, or do I need to run some
> > sort of check at every subtraction to make sure that my float does not
> > deviate? I'm not sure I know even how to do that.

>
> I still don't quite understand your problem. If you think your float is
> deviating from the correct value, that implies you know what the correct
> value should be. How do you know what the correct value should be?
>
> I should also mention that of course your answer will deviate, due to the
> finite precision of floats.


I'm adding and subtracting things with 1 decimal point; I can do the
math for an given case trivially. Are you suggesting that I don't
know what the exact floating point quantity should be?

> Obviously not. As you've already shown, the correct value is
> 0.70000000000000018, not 0.69999999999999996 (the closest floating point
> value to 0.7).


The case I cited that you disliked is practically the same as the one
above; sorry I posted one where the value of the variable was
unstated.

> > I was unable to find solutions when searching the web because all of the
> > hits I got were discussing display issues, which I'm not concerned with.

>
> Have you read this?http://docs.sun.com/source/806-3568/ncg_goldberg.html


I started to, but didn't get through the whole thing. I'm saving that
for bedtime reading after finals.

Anyway, whether or not it's stated in that document, as I presume it
is, the problem I have resulted from a different level of precision
for numbers <1 and numbers >1. i.e.
1.1000000000000001
.90000000000000002
so the larger the number (in powers of ten), the further off the
difference between two numbers will be. I suppose that's why the
"guard digits" did nothing for me - I subtracted a "real number" from
a guard digit. Still, this seems like very unintuitive behavior; it
would be natural to truncate the subtraction at the last digit of
1.1... rather than including the uncertain digit in the final answer.

> --
> Steven.


 
Reply With Quote
 
Paddy
Guest
Posts: n/a
 
      12-14-2007
On Dec 14, 3:22 am, Keflavich <(E-Mail Removed)> wrote:
> On Dec 13, 5:52 pm, Steven D'Aprano <st...@REMOVE-THIS-
>
> cybersource.com.au> wrote:
> > On Thu, 13 Dec 2007 14:30:18 -0800, Keflavich wrote:
> > > Hey, I have a bit of code that died on a domain error when doing an
> > > arcsin, and apparently it's because floating point subtraction is having
> > > problems.

>

I've come to understand that
1. Floats are fuzzy around the edges.
2. Never do: "The difference between two floats is equal to..."
Do: "The difference between two floats is in the range ..."
But even 2 needs some serious thinking if the range of float values
being
compared is large.

Its unscientific, but floats need more respect.

- Paddy.


 
Reply With Quote
 
Nikos Vergas
Guest
Posts: n/a
 
      12-14-2007
> Solved: used round(number,12) in this case for all of the operands of
> my arcsines. Not pretty, but at least VIM made it easy...


You might have the same problem though:

>>> round(1.000340100000325235000000235,13)

1.0003401000003
>>> round(1.000340100000325235000000235,12)

1.0003401000000001
 
Reply With Quote
 
Gabriel Genellina
Guest
Posts: n/a
 
      12-14-2007
En Fri, 14 Dec 2007 00:22:18 -0300, Keflavich <(E-Mail Removed)>
escribió:

> On Dec 13, 5:52 pm, Steven D'Aprano <st...@REMOVE-THIS-
> cybersource.com.au> wrote:
>> On Thu, 13 Dec 2007 14:30:18 -0800, Keflavich wrote:
>> > Hey, I have a bit of code that died on a domain error when doing an
>> > arcsin, and apparently it's because floating point subtraction is

>> having
>> > problems.

>>
>> I'm not convinced that your diagnosis is correct. Unless you're using
>> some weird, uncommon hardware, it's unlikely that a bug in the floating
>> point subtraction routines has escaped detection. (Unlikely, but not
>> impossible.) Can you tell us what values give you incorrect results?

>
> Here's a better (more complete) example [btw, I'm using ipython]:
>
> In [39]: x = 3.1 + .6
> In [40]: y = x - 3.1
> In [41]: y == 6
> Out[41]: False
> In [42]: x == 3.7
> Out[42]: True
> In [43]: 3.1+.6-3.1 == .6
> Out[43]: False
> In [45]: (3.1+.6-3.1)/.6
> Out[45]: 1.0000000000000002
> In [46]: (3.1+.6-3.1)/.6 == 1
> Out[46]: False
> In [47]: (3.1+.6-3.1)/.6 > 1
> Out[47]: True


Let's see how these float numbers are represented: significand *
2**exponent where 1<=significand<2 (like scientific notation but using
base 2, not base 10) and with 53 binary digits available for the
significand (this is more or less the format used by IEEE754 floating
point, implemented in hardware on all platforms where Python is available
and I know of, except maybe some cell phones):

3.1 = 3.1000000000000001 = 0.77500000000000002 x 2**2 =
1.100011001100110011001100110011001100110011001100 1101 x 2**1

0.6 = 0.59999999999999998 = 0.59999999999999998 x 2**0 =
1.001100110011001100110011001100110011001100110011 0011 x 2**-1

3.1+0.6 = 3.7000000000000002 = 0.92500000000000004 x 2**2 =
1.110110011001100110011001100110011001100110011001 1010 x 2**1

3.1+0.6-3.1 = 0.60000000000000009 = 0.60000000000000009 x 2**0 =
1.001100110011001100110011001100110011001100110011 0100 x 2**-1

where the 1.xxxx are binary fractions.

Let's compute 3.1+0.6 using the binary form:

3.1 = 11.00011001100110011001100110011001100110011001100 1101
0.6 = .1001100110011001100110011001100110011001100110011 00 11
sum = 11.10110011001100110011001100110011001100110011001 1010

Notice that, to align both numbers at their decimal point (hmm, binary
point!), we had to discard the two less significant binary digits of 0.6.
The result, however, is still the same as if we had done the computation
exactly and then rounded to 53 significant binary digits. (that's why x ==
3.7 is True in your example above).

Now let's substract 3.1 from the result:
sum = 11.10110011001100110011001100110011001100110011001 1010
3.1 = 11.00011001100110011001100110011001100110011001100 1101
dif = .1001100110011001100110011001100110011001100110011 01 __

There are 51 significant bits there; as we use 53 bits in the
representation, the two less significant bits are set to 0 (they're
"unknown", in fact). We lose those 2 bits because we are substracting
numbers close to each other (this is known as cancellation). The error is
only 1 unit of the least significant binary digit (1x2**-53) but enough to
make that number different to the representation of 0.6 (they differ by
the least possible amount).

Note that this is the best result you can get with a limited precision of
53 bits, and it's not even Python who computes the value, but the
underlying C math library (and very likely, using the hardware). IEEE754
defines substraction very precisely so people can get reliable results on
any platform, and this is not an exception.

>> > I know about the impossibility of storing floating point
>> > numbers precisely, but I was under the impression that the standard

>> used
>> > for that last digit would prevent subtraction errors from compounding.

>>
>> What gave you that impression? Are you referring to guard digits?

>
> I believe that's what I was referring to; I have only skimmed the
> topic, haven't gone into a lot of depth


Substraction of numbers of similar magnitude is often a problem, like
addition of very dissimilar numbers.

>> I should also mention that of course your answer will deviate, due to
>> the
>> finite precision of floats.

>
> I'm adding and subtracting things with 1 decimal point; I can do the
> math for an given case trivially. Are you suggesting that I don't
> know what the exact floating point quantity should be?


Notice that those values have an exact *decimal* representation but are
*periodic* written in binary form, as you can see from above. Any finite
binary representation must be approximate then. It's like trying to write
1/3 in decimal form, you can't do that exactly without requiring infinite
digits.

>> Have you read this?http://docs.sun.com/source/806-3568/ncg_goldberg.html

>
> I started to, but didn't get through the whole thing. I'm saving that
> for bedtime reading after finals.


At least overview the topics - you might not be interested in the theorem
proofs and some details, but the consequences and discussion are worth
reading.

> Anyway, whether or not it's stated in that document, as I presume it
> is, the problem I have resulted from a different level of precision
> for numbers <1 and numbers >1. i.e.
> 1.1000000000000001
> .90000000000000002
> so the larger the number (in powers of ten), the further off the
> difference between two numbers will be. I suppose that's why the
> "guard digits" did nothing for me - I subtracted a "real number" from
> a guard digit. Still, this seems like very unintuitive behavior; it
> would be natural to truncate the subtraction at the last digit of
> 1.1... rather than including the uncertain digit in the final answer.


Well, this is what "floating" point means: the number of significant
digits after the decimal point is not fixed. You appear to want a "fixed
point" approach; for some applications fixed point is better suited (money
accounting, where 4 decimal places are usually enough) but for general,
wide range numbers, floating point is better.

--
Gabriel Genellina

 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
subtraction of floating point numbers Jaroslav Dobrek Python 2 02-24-2012 10:16 AM
floating point rounding error? Roger Pack Ruby 8 02-22-2009 11:28 PM
Floating point subtraction with FLT_MAX error spooler123@gmail.com C Programming 17 10-27-2007 01:17 AM
Floating point rounding error Mukesh_Singh_Nick@yahoo.com C Programming 15 07-02-2007 08:11 AM
floating point subtraction question Nicholas Rahn Ruby 4 11-27-2005 11:39 PM



Advertisments