Ask Your Question

Revision history [back]

click to hide/show revision 1
initial version

answered 2011-03-01 16:14:39 -0600

DSM gravatar image

Yep, that's actually what's supposed to happen, because of how floating-point arithmetic works. Here's a gentle guide 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

and avoid precision issues. Does that make sense?

Yep, that's actually what's supposed to happen, because of how floating-point arithmetic works. Here's a gentle guide 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?