ASKSAGE: Sage Q&A Forum - Individual question feedhttps://ask.sagemath.org/questions/Q&A Forum for SageenCopyright Sage, 2010. Some rights reserved under creative commons license.Tue, 01 Mar 2011 16:30:16 -0600rounding bug?https://ask.sagemath.org/question/7963/rounding-bug/Hi all:
In a notebook window i tell Sage
w = 0.4
while w > 0.1:
w = w - 0.1
print w
print w==0
and it outputs
0.3000000000000000
0.200000000000000
0.100000000000000
2.77555756156289e-17
False
Is this supposed to happen?
Thanks for your attention.
Alex
Tue, 01 Mar 2011 15:28:02 -0600https://ask.sagemath.org/question/7963/rounding-bug/Answer by DSM for <p>Hi all:</p>
<p>In a notebook window i tell Sage</p>
<pre><code>w = 0.4
while w > 0.1:
w = w - 0.1
print w
print w==0
</code></pre>
<p>and it outputs</p>
<pre><code>0.3000000000000000
0.200000000000000
0.100000000000000
2.77555756156289e-17
False
</code></pre>
<p>Is this supposed to happen?</p>
<p>Thanks for your attention.
Alex</p>
https://ask.sagemath.org/question/7963/rounding-bug/?answer=12160#post-id-12160Yep, that's actually what's supposed to happen, because of how floating-point arithmetic works. Here's a [gentle guide](http://floating-point-gui.de/) to the issues. Using comparisons with the results of arithmetic operations is always a bit dangerous because of limited precision.. note that the final error you get is ~2^(-55).
Your number "0.1" is living in the Real Field with 53 bits of precision:
sage: w = 0.4
sage: w
0.400000000000000
sage: parent(w)
Real Field with 53 bits of precision
and so the number isn't actually exactly 0.4:
sage: (0.4).str(base=2)
'0.011001100110011001100110011001100110011001100110011010'
You can round the result to a given number of decimal digits if you want:
sage: w = 0.4
sage: dw = 0.1
sage: ((((w - dw)-dw)-dw)-dw)
2.77555756156289e-17
sage: round(((((w - dw)-dw)-dw)-dw), ndigits=10)
0.0
Or use more precision:
sage: RF = RealField(100)
sage: RF
Real Field with 100 bits of precision
sage: w = RF("0.4")
sage: w.str(base=2)
'0.01100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011001101'
sage: dw = RF("0.1")
sage: while w > dw:
....: w = w - dw
....: print w
....:
0.30000000000000000000000000000
0.20000000000000000000000000000
0.10000000000000000000000000000
1.9721522630525295135293214132e-31
sage: print w==0
False
but that will only make the error smaller, in this case ~2^(-102), not vanish. You can use the Real Interval Field if you want to more accurately keep track of these differences:
sage: w = RIF("0.4")
sage: dw = RIF("0.1")
sage: while w > dw:
....: w = w - dw
....: print w
....:
0.3000000000000000?
0.2000000000000000?
0.1000000000000000?
where the question mark indicates uncertainty. But now the loop doesn't go all the way down to zero, because on the last step of the loop it's no longer true that 0.10000.. > 0.10000.. (See what I mean about trouble comparing?) Once you get two zeros, though, you can compare them:
sage: w = RIF("0.4")
sage: dw = RIF("0.1")
sage: ((((w - dw)-dw)-dw)-dw)
0.?e-16
sage: z = ((((w - dw)-dw)-dw)-dw)
sage: z == 0 # it's not _equal_ to 0, but:
False
sage: 0 in z # 0 is in the interval!
True
And if you really want 4/10, and not a floating-point approximation to it, you can work with the exact values:
sage: w = 4/10
sage: dw = 1/10
sage: while w >= dw:
....: w = w - dw
....: print w
....:
3/10
1/5
1/10
0
sage: w == 0
True
and avoid precision issues. Does that make sense?Tue, 01 Mar 2011 16:14:39 -0600https://ask.sagemath.org/question/7963/rounding-bug/?answer=12160#post-id-12160Comment by kcrisman for <p>Yep, that's actually what's supposed to happen, because of how floating-point arithmetic works. Here's a <a href="http://floating-point-gui.de/">gentle guide</a> to the issues. Using comparisons with the results of arithmetic operations is always a bit dangerous because of limited precision.. note that the final error you get is ~2^(-55).</p>
<p>Your number "0.1" is living in the Real Field with 53 bits of precision:</p>
<pre><code>sage: w = 0.4
sage: w
0.400000000000000
sage: parent(w)
Real Field with 53 bits of precision
</code></pre>
<p>and so the number isn't actually exactly 0.4:</p>
<pre><code>sage: (0.4).str(base=2)
'0.011001100110011001100110011001100110011001100110011010'
</code></pre>
<p>You can round the result to a given number of decimal digits if you want:</p>
<pre><code>sage: w = 0.4
sage: dw = 0.1
sage: ((((w - dw)-dw)-dw)-dw)
2.77555756156289e-17
sage: round(((((w - dw)-dw)-dw)-dw), ndigits=10)
0.0
</code></pre>
<p>Or use more precision:</p>
<pre><code>sage: RF = RealField(100)
sage: RF
Real Field with 100 bits of precision
sage: w = RF("0.4")
sage: w.str(base=2)
'0.01100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011001101'
sage: dw = RF("0.1")
sage: while w > dw:
....: w = w - dw
....: print w
....:
0.30000000000000000000000000000
0.20000000000000000000000000000
0.10000000000000000000000000000
1.9721522630525295135293214132e-31
sage: print w==0
False
</code></pre>
<p>but that will only make the error smaller, in this case ~2^(-102), not vanish. You can use the Real Interval Field if you want to more accurately keep track of these differences:</p>
<pre><code>sage: w = RIF("0.4")
sage: dw = RIF("0.1")
sage: while w > dw:
....: w = w - dw
....: print w
....:
0.3000000000000000?
0.2000000000000000?
0.1000000000000000?
</code></pre>
<p>where the question mark indicates uncertainty. But now the loop doesn't go all the way down to zero, because on the last step of the loop it's no longer true that 0.10000.. > 0.10000.. (See what I mean about trouble comparing?) Once you get two zeros, though, you can compare them:</p>
<pre><code>sage: w = RIF("0.4")
sage: dw = RIF("0.1")
sage: ((((w - dw)-dw)-dw)-dw)
0.?e-16
sage: z = ((((w - dw)-dw)-dw)-dw)
sage: z == 0 # it's not _equal_ to 0, but:
False
sage: 0 in z # 0 is in the interval!
True
</code></pre>
<p>And if you really want 4/10, and not a floating-point approximation to it, you can work with the exact values:</p>
<pre><code>sage: w = 4/10
sage: dw = 1/10
sage: while w >= dw:
....: w = w - dw
....: print w
....:
3/10
1/5
1/10
0
sage: w == 0
True
</code></pre>
<p>and avoid precision issues. Does that make sense?</p>
https://ask.sagemath.org/question/7963/rounding-bug/?comment=22037#post-id-22037I think you are just more comprehensive than I am :)Tue, 01 Mar 2011 16:28:57 -0600https://ask.sagemath.org/question/7963/rounding-bug/?comment=22037#post-id-22037Answer by kcrisman for <p>Hi all:</p>
<p>In a notebook window i tell Sage</p>
<pre><code>w = 0.4
while w > 0.1:
w = w - 0.1
print w
print w==0
</code></pre>
<p>and it outputs</p>
<pre><code>0.3000000000000000
0.200000000000000
0.100000000000000
2.77555756156289e-17
False
</code></pre>
<p>Is this supposed to happen?</p>
<p>Thanks for your attention.
Alex</p>
https://ask.sagemath.org/question/7963/rounding-bug/?answer=12157#post-id-12157In a word, yes. You are using floating point accuracy, which means that 0.4 is not actually 0.4, but rather the closest the computer can get to it with a certain accuracy. In Sage, that default is 53 bits of precision:
sage: w.parent()
Real Field with 53 bits of precision
sage: w.str(truncate=False)
'0.40000000000000002'
For what you are looking for, you need arbitrary precision - which rationals would give you.
sage: w = 4/10
sage: while w > 1/10:
....: w = w - 1/10
....: print w
....:
3/10
1/5
1/10
which also shows that your loop 'should have' ended earlier, but didn't because the number that printed `0.10000` actually was slightly larger than `0.1` in the computer's internal memory, so the loop iterated one more time.
I'm not an expert on numerical analysis or machine representation of numbers, but the Internet should have some good explanations of these things.Tue, 01 Mar 2011 16:03:16 -0600https://ask.sagemath.org/question/7963/rounding-bug/?answer=12157#post-id-12157Comment by DSM for <p>In a word, yes. You are using floating point accuracy, which means that 0.4 is not actually 0.4, but rather the closest the computer can get to it with a certain accuracy. In Sage, that default is 53 bits of precision:</p>
<pre><code>sage: w.parent()
Real Field with 53 bits of precision
sage: w.str(truncate=False)
'0.40000000000000002'
</code></pre>
<p>For what you are looking for, you need arbitrary precision - which rationals would give you.</p>
<pre><code>sage: w = 4/10
sage: while w > 1/10:
....: w = w - 1/10
....: print w
....:
3/10
1/5
1/10
</code></pre>
<p>which also shows that your loop 'should have' ended earlier, but didn't because the number that printed <code>0.10000</code> actually was slightly larger than <code>0.1</code> in the computer's internal memory, so the loop iterated one more time. </p>
<p>I'm not an expert on numerical analysis or machine representation of numbers, but the Internet should have some good explanations of these things.</p>
https://ask.sagemath.org/question/7963/rounding-bug/?comment=22038#post-id-22038@kcrisman: I promise I didn't see yours before writing mine, so any similarity is merely driven by the subject matter. :^)Tue, 01 Mar 2011 16:17:45 -0600https://ask.sagemath.org/question/7963/rounding-bug/?comment=22038#post-id-22038Comment by kcrisman for <p>In a word, yes. You are using floating point accuracy, which means that 0.4 is not actually 0.4, but rather the closest the computer can get to it with a certain accuracy. In Sage, that default is 53 bits of precision:</p>
<pre><code>sage: w.parent()
Real Field with 53 bits of precision
sage: w.str(truncate=False)
'0.40000000000000002'
</code></pre>
<p>For what you are looking for, you need arbitrary precision - which rationals would give you.</p>
<pre><code>sage: w = 4/10
sage: while w > 1/10:
....: w = w - 1/10
....: print w
....:
3/10
1/5
1/10
</code></pre>
<p>which also shows that your loop 'should have' ended earlier, but didn't because the number that printed <code>0.10000</code> actually was slightly larger than <code>0.1</code> in the computer's internal memory, so the loop iterated one more time. </p>
<p>I'm not an expert on numerical analysis or machine representation of numbers, but the Internet should have some good explanations of these things.</p>
https://ask.sagemath.org/question/7963/rounding-bug/?comment=22036#post-id-22036Obviously! I've actually learned quite a bit by reading up on this over the past year or so; it's very interesting. Not as good as algebra, but at least numerical analysis has some redeeming value after all.Tue, 01 Mar 2011 16:30:16 -0600https://ask.sagemath.org/question/7963/rounding-bug/?comment=22036#post-id-22036Comment by kcrisman for <p>In a word, yes. You are using floating point accuracy, which means that 0.4 is not actually 0.4, but rather the closest the computer can get to it with a certain accuracy. In Sage, that default is 53 bits of precision:</p>
<pre><code>sage: w.parent()
Real Field with 53 bits of precision
sage: w.str(truncate=False)
'0.40000000000000002'
</code></pre>
<p>For what you are looking for, you need arbitrary precision - which rationals would give you.</p>
<pre><code>sage: w = 4/10
sage: while w > 1/10:
....: w = w - 1/10
....: print w
....:
3/10
1/5
1/10
</code></pre>
<p>which also shows that your loop 'should have' ended earlier, but didn't because the number that printed <code>0.10000</code> actually was slightly larger than <code>0.1</code> in the computer's internal memory, so the loop iterated one more time. </p>
<p>I'm not an expert on numerical analysis or machine representation of numbers, but the Internet should have some good explanations of these things.</p>
https://ask.sagemath.org/question/7963/rounding-bug/?comment=22039#post-id-22039As a further comment, pretty much any computer mathematics software is going to have this limitation. Some have only this type of representation, while others have both exact and various precision number types.Tue, 01 Mar 2011 16:05:02 -0600https://ask.sagemath.org/question/7963/rounding-bug/?comment=22039#post-id-22039