This I imagine is extremely simple - but why in the following are the two values for y not == 0? I thought the whole point of the decimal module was to get rid of the float dust ...The following is an extremely simplified version of a mathematical routine that passes numbers around as variables.
from decimal import *
getcontext().prec = 2
q = Decimal(0.01)
x = Decimal(0.10) * Decimal(0.10)
y = Decimal(x) - Decimal(q)
print(x,y, Decimal(y))
'''
x== 0.010
y== -2.1E-19
Decimal(y) == -2.1E-19
'''
Try specifying the numbers as strings
>>> Decimal('0.10') * Decimal('0.10') - Decimal('0.0100')
>>> Decimal('0.000')
The float literal 0.10 is not precisely the mathematical number 0.10, using it to initialize Decimal doesn't avoid the float precision problem.
Instead, using strings to initialize Decimal can give you expected result:
x = Decimal('0.10') * Decimal('0.10')
y = Decimal(x) - Decimal('0.010')
This is a more detailed explanation of the point made in existing answers.
You really do need to get rid of the numeric literals such as 0.1 if you want exact decimal arithmetic. The numeric literals will typically be represented by IEEE 754 64-bit binary floating point numbers.
The closest such number to 0.1 is 0.1000000000000000055511151231257827021181583404541015625. Its square is 0.01000000000000000111022302462515657123851077828659396139564708135883709660962637144621112383902072906494140625, which is not the same as the closest to 0.01, 0.01000000000000000020816681711721685132943093776702880859375.
You can get a clearer view of what is going on by removing the prec =2 context, allowing more precise output:
from decimal import *
q = Decimal(0.01)
x = Decimal(0.10) * Decimal(0.10)
y = Decimal(x) - Decimal(q)
print(q)
print(x)
print(y)
Output:
0.01000000000000000020816681711721685132943093776702880859375
0.01000000000000000111022302463
9.020562075127831486705690622E-19
If you had used string literals, as suggested by the other responses, the conversion to Decimal would have been done directly, without going through binary floating point. Both 0.1 and 0.01 are exactly representable in Decimal, so there would be no rounding error.
Related
Trying to add zeroes and a random digit in floats in Python.
I have a code that just loops through and shows the cumulative frequency at each step and the final sum should be 1 but it isn't working.
Here is the output:
0.11
0.197
0.279
0.35700000000000004
0.43000000000000005
0.502
0.569
0.6299999999999999
0.6829999999999999
0.723
0.761
0.794
0.8240000000000001
0.8520000000000001
0.8790000000000001
0.9040000000000001
0.9270000000000002
0.9470000000000002
0.9630000000000002
0.9770000000000002
0.9870000000000002
0.9961000000000002
1.0005000000000002
1.0032
1.0056
1.0077
1.0077
I think this is being caused by the long row of zeroes in some numbers like 0.35700000000000004 in the 4th one. Also, many values are changing like in the first step it should be 0.11 and not 0.197.
Example code:
for i in AlphabetWeights:
count = i+count
print(count)
This is not an arithmetic issue.. You can see that your truncation error is happening O(1e-16), as is expected. What you describe as an 'error' is on the order of 1e-3. You even see that your truncation error is cancelling out at times.. no way that your deviation of 0.0077 from the desired result 1.0 is due to floating-point arithmetic
like in the first step it should be 0.11 and not 0.197.
Check your code for bugs.. this is not floating-point arithmetic
You can use Decimal in this case. The decimal module provides support for fast correctly-rounded decimal floating-point arithmetic. Check this link: Python Decimal
Check this code:
from decimal import *
getcontext().prec = 6
Decimal(1) / Decimal(7)
Decimal('0.142857')
getcontext().prec = 28
Decimal(1) / Decimal(7)
Decimal('0.1428571428571428571428571429')
for example:
x = 0.1
y = 0.1
z = 0.1
s = x + y + z
print(s)
the output will be :
0.30000000000000004
but
import decimal
from decimal import Decimal
x = Decimal('0.1')
y = Decimal('0.1')
z = Decimal('0.1')
s = x + y + z
print(s)
the output will be :
0.3
I am aware of the nature of floating point math but I still find the following surprising:
from fractions import Fraction
print(Fraction(0.2)) # -> 3602879701896397/18014398509481984
print(Fraction(str(0.2))) # -> 1/5
print(Fraction(0.2)==Fraction(str(0.2))) # returns False
print(0.2 == float(str(0.2))) # but this returns True!
From the documentation I could not find anything that would explain that. It does state:
...In addition, any string that represents a finite value and is
accepted by the float constructor is also accepted by the Fraction
constructor...
but to me this implies a similar behavior to float() which I just do not see as shown above.
Is there any explanation for this?
It is important to note that the behavior shown above is not specific to the value (0.2) but rather general; everything I tried behaved the same way.
Interestingly enough:
from fractions import Fraction
for x in range(1, 257):
if Fraction(str(1/x))==Fraction(1/x):
print(x)
prints only the powers of 2 that are smaller than the selected upper bound:
1
2
4
8
16
32
64
128
256
Have a look at the def __new__(): implementation in fractions.py, if a string is given:
The regex _RATIONAL_FORMAT ( see link if you are interested in the parsing part) puts out numerator as 0 and decimal as 2
Start quote from fractions.py source, with comments by me
elif isinstance(numerator, str):
# Handle construction from strings.
m = _RATIONAL_FORMAT.match(numerator)
if m is None:
raise ValueError('Invalid literal for Fraction: %r' %
numerator)
numerator = int(m.group('num') or '0') # 0
denom = m.group('denom')
if denom: # not true for your case
denominator = int(denom)
else: # we are here
denominator = 1
decimal = m.group('decimal') # yep: 2
if decimal:
scale = 10**len(decimal) # thats 10^1
numerator = numerator * scale + int(decimal) # thats 0 * 10^1+0 = 10
denominator *= scale # thats 1*2
exp = m.group('exp')
if exp: # false
exp = int(exp)
if exp >= 0:
numerator *= 10**exp
else:
denominator *= 10**-exp
if m.group('sign') == '-': # false
numerator = -numerator
else:
raise TypeError("argument should be a string "
"or a Rational instance")
end quote from source
So '0.2' is parsed to 2 / 10 = 0.2 exactly, not its nearest float approximation wich my calculater puts out at 0,20000000000000001110223024625157
Quintessential: they are not simply using float( yourstring ) but are parsing and calculating the string itself, that is why both differ.
If you use the same constructor and provide a float or decimal the constructor uses the builtin as_integer_ratio() to get numerator and denominator as representation of that number.
The closest the float representation comes to 0.2 is 0,20000000000000001110223024625157 which is exactly what the as_integer_ratio() method returns nominator and denominator for.
As eric-postpischil
and mark-dickinson pointed out, this float value is limited by its binary representations to "close to 0.2". When put into str() will be truncated to exact '0.2' - hence the differences between
print(Fraction(0.2)) # -> 3602879701896397/18014398509481984
print(Fraction(str(0.2))) # -> 1/5
In print(Fraction(0.2)), the source text 0.2 is converted to a floating-point value. The result of this conversion is exactly 0.200000000000000011102230246251565404236316680908203125, or 3602879701896397/18014398509481984. This value is then passed to Fraction, which produces the same value represented as a rational number.
In print(Fraction(str(0.2))), 0.2 is again converted to a floating-point value, yielding the number above. Then str converts it to a string. In current Python versions, when a floating-point value is converted to a string, Python does not generally produce the exact mathematical value. Instead, it produces the just enough digits so that converting the string back to floating-point produces the input number. In this case, that results in “0.2”. So the string “0.2” is passed to Fraction. Then Fraction analyzes “0.2” and determines it is 1/5.
Notice the last digit in the denominator. It appears the fractions module takes this into consideration when storing the object internally, but when used in operations python can round.
from fractions import Fraction
Fraction(3602879701896397, 18014398509481985) == Fraction(1, 5) # True
Fraction(3602879701896397, 18014398509481984) == Fraction(1, 5) # False
3602879701896397 / 18014398509481985 == 0.2 # True
3602879701896397 / 18014398509481984 == 0.2 # True
Now the question of why the fractions module chooses an approximation (i.e. 18014398509481984 instead of correct 18014398509481985) is not one I can answer.
I was wondering if anybody knew of a quick way in python to check and see if a fraction gives a repeating decimal.
I have a small function that takes in two numbers and divides them. If the quotient is a repeating decimal I would like to round to 2 decimal places and if the quotient is not repeating I would like to round to just one
Example:
800/600 = 1.33333333333333 which would equal 1.33
900/600 = 1.5 would stay as 1.5
I know that I need to use the two statements for the two types of rounding
output = "{:.2f}".format(float(num))
output = "{:,}".format(float(num))
but I am having trouble with the if statement to direct to one or the other.
Can anybody help with some insight?
Use the fractions module, which implements exact rational arithmetic:
import fractions
# fractions.Fraction instances are automatically put in lowest terms.
ratio = fractions.Fraction(numerator, denominator)
You can then inspect the denominator of the result:
def is_repeating(fraction):
denom = fraction.denominator
while not (denom % 2):
denom //= 2
while not (denom % 5):
denom //= 5
return denom != 1
Just a workaround using regex :)
import re
result = str(800/600)
# result = str(900/600)
repeating_pair = re.escape(result.split('.')[1][:2])
check_within = result.split('.')[1][2:]
if re.match(repeating_pair, check_within):
print("{:.2f}".format(float(result)))
else:
print("{:.1f}".format(float(result)))
Output:
1.33
And for 900/600
1.5
Try this: Just use brute force. Since you want only 2 decimal places. Just divide and then test it when it is rounded to 0 and 1 decimal place and see where it stops being unique. If it is not unique at this point, then round to 2 decimal places.
def f(x):
if x == round(x,0):
return '{:.0f}'.format(x)
elif x == round(x,1):
return '{:.1f}'.format(x)
else:
return round(x,2)
y = [1, 2, 3, 3/2, 1/9, 8/9, 1/11, 12/11, 10/11, 14/13, 1/3]
for item in y:
print(f(item))
Output:
1
2
3
1.5
0.11
0.89
0.09
1.09
0.91
1.08
0.33
>>>
repeating decimal
There are only 10 fractions that can be written as some repeated digit - .(0), .(1), ... .(9). Thus, if you only care about repeating pattern starting right after decimal point, you only need to check against those cases.
All those numbers (and only them) give an integer if multiplied by 9.
Thus, if (9 * numenator) % denominator == 0, you'll print 2 digits.
You'll probably want to exclude .(0) pattern though. To do that, test if your fraction is in fact an integer - numenator % denominator == 0.
Also check out fractions module in case you have some wheels to reinvent.
Of course, if you only have your number as a float, there is some ambiguity about what numenator and denominator are, because floats don't actually store rational numbers like 1/3. You can experiment with fractions's .limit_denominator() to choose something that works for your case.
This I imagine is extremely simple - but why in the following are the two values for y not == 0? I thought the whole point of the decimal module was to get rid of the float dust ...The following is an extremely simplified version of a mathematical routine that passes numbers around as variables.
from decimal import *
getcontext().prec = 2
q = Decimal(0.01)
x = Decimal(0.10) * Decimal(0.10)
y = Decimal(x) - Decimal(q)
print(x,y, Decimal(y))
'''
x== 0.010
y== -2.1E-19
Decimal(y) == -2.1E-19
'''
Try specifying the numbers as strings
>>> Decimal('0.10') * Decimal('0.10') - Decimal('0.0100')
>>> Decimal('0.000')
The float literal 0.10 is not precisely the mathematical number 0.10, using it to initialize Decimal doesn't avoid the float precision problem.
Instead, using strings to initialize Decimal can give you expected result:
x = Decimal('0.10') * Decimal('0.10')
y = Decimal(x) - Decimal('0.010')
This is a more detailed explanation of the point made in existing answers.
You really do need to get rid of the numeric literals such as 0.1 if you want exact decimal arithmetic. The numeric literals will typically be represented by IEEE 754 64-bit binary floating point numbers.
The closest such number to 0.1 is 0.1000000000000000055511151231257827021181583404541015625. Its square is 0.01000000000000000111022302462515657123851077828659396139564708135883709660962637144621112383902072906494140625, which is not the same as the closest to 0.01, 0.01000000000000000020816681711721685132943093776702880859375.
You can get a clearer view of what is going on by removing the prec =2 context, allowing more precise output:
from decimal import *
q = Decimal(0.01)
x = Decimal(0.10) * Decimal(0.10)
y = Decimal(x) - Decimal(q)
print(q)
print(x)
print(y)
Output:
0.01000000000000000020816681711721685132943093776702880859375
0.01000000000000000111022302463
9.020562075127831486705690622E-19
If you had used string literals, as suggested by the other responses, the conversion to Decimal would have been done directly, without going through binary floating point. Both 0.1 and 0.01 are exactly representable in Decimal, so there would be no rounding error.
The following code snippet is giving 6 as a result:
import math
number = (1 - 0.99) * 500
math.ceil(number)
while the (mathematically) correct answer would be 5. Presumably this is a rounding problem - what is the best way to enforce the correct solution?
Presumably this is a rounding problem
Yes:
>>> 1 - 0.99
0.010000000000000009
>>> (1 - 0.99) * 500
5.000000000000004
what is the best way to enforce the correct solution?
You could use a decimal.Decimal instead of a float:
>>> from decimal import Decimal
>>> import math
>>> (1 - Decimal("0.99")) * 500
Decimal('5.00')
>>> math.ceil((1 - Decimal("0.99")) * 500)
5.0
It's a floating-point error since some numbers can't be represented exactly (infinitely many numbers have to be represented using a finite number of bits -- there has to be some trade-offs). This is why you lose some precision with floating point operations:
>>> 1-0.99
0.010000000000000009
Try Decimal:
>>> from decimal import Decimal as d
>>> result = (1 - d("0.99")) * 500
>>> result
Decimal('5.00')
>>> math.ceil(result)
5.0
Edit
It may look like all the numbers have exact representations:
>>> a = 1.0; b = 0.99; c = 0.01
>>> a, b, c
(1.0, 0.99, 0.01)
So this result might seem surprising:
>>> a - b
0.010000000000000009
>>> a - b == c
False
But it's just the precision and rounding errors that accumulate. Here are the same numbers and calculation, but showing more digits:
>>> def o(f): return "%.30f" % f
>>> o(a)
'1.000000000000000000000000000000'
>>> o(b)
'0.989999999999999991118215802999'
>>> o(c)
'0.010000000000000000208166817117'
>>> o(a-b)
'0.010000000000000008881784197001'
Python 2.7 rounds to 17 significant digits. It is a different model from real math.
The given answers are correct, this is a case of rounding error. However, I think it would be useful to include why this happens.
In hardware, floating point numbers are base 2 (AKA binary). The problem is that most decimal fractions cannot be represented exactly as binary fractions. The translation of that is (in general) floating point numbers are only approximated by the binary floating point numbers actually stored in the machine.