I try get ration of variable and get unexpected result. Can somebody explain this?
>>> value = 3.2
>>> ratios = value.as_integer_ratio()
>>> ratios
(3602879701896397, 1125899906842624)
>>> ratios[0] / ratios[1]
3.2
I using python 3.3
But I think that (16, 5) is much better solution
And why it correct for 2.5
>>> value = 2.5
>>> value.as_integer_ratio()
(5, 2)
Use the fractions module to simplify fractions:
>>> from fractions import Fraction
>>> Fraction(3.2)
Fraction(3602879701896397, 1125899906842624)
>>> Fraction(3.2).limit_denominator()
Fraction(16, 5)
From the Fraction.limit_denominator() function:
Finds and returns the closest Fraction to self that has denominator at most max_denominator. This method is useful for finding rational approximations to a given floating-point number
Floating point numbers are limited in precision and cannot represent many numbers exactly; what you see is a rounded representation, but the real number is:
>>> format(3.2, '.50f')
'3.20000000000000017763568394002504646778106689453125'
because a floating point number is represented as a sum of binary fractions; 1/5 can only be represented by adding up 1/8 + 1/16 + 1/128 + more binary fractions for increasing exponents of two.
It's not 16/5 because 3.2 isn't 3.2 exactly... it's a floating point rough approximation of it... eg: 3.20000000000000017764
While using the fractions module, it is better to provide a string instead of a float to avoid floating point representation issues.
For example, if you pass '3.2' instead of 3.2 you get your desired result:
In : fractions.Fraction('3.2')
Out: Fraction(16, 5)
If you already have the value stored in a variable, you can use string formatting as well.
In : value = 3.2
In : fractions.Fraction(f'{value:.2f}')
Out: Fraction(16, 5)
Related
This question already has answers here:
Is floating point math broken?
(31 answers)
Closed 2 years ago.
Parts of this question have been addressed elsewhere (e.g. is floating point math broken?).
The following reveals a difference in the way numbers are generated by division vs multiplication:
>>> listd = [i/10 for i in range(6)]
>>> listm = [i*0.1 for i in range(6)]
>>> print(listd)
[0.0, 0.1, 0.2, 0.3, 0.4, 0.5]
>>> print(listm)
[0.0, 0.1, 0.2, 0.30000000000000004, 0.4, 0.5]
In the second case, 0.3 has a rounding error of about 1e-16, double floating point precision.
But I don't understand three things about the output:
Since the only numbers here exactly representable in binary are 0.0 and 0.5, why aren't those the only exact numbers printed above?
Why do the two list comprehensions evaluate differently?
Why are the two string representations of the numbers different, but not their binary representations?
>>> def bf(x):
return bin(struct.unpack('#i',struct.pack('!f',float(x)))[0])
>>> x1 = 3/10
>>> x2 = 3*0.1
>>> print(repr(x1).ljust(20), "=", bf(x1))
>>> print(repr(x2).ljust(20), "=", bf(x2))
0.3 = -0b1100101011001100110011011000010
0.30000000000000004 = -0b1100101011001100110011011000010
Answering each question:
Since the only numbers here exactly representable in binary are 0.0 and 0.5, why aren't those the only exact numbers printed above?
Python rounds off the display of any floating point number to the shortest literal that produces the same value when evaluated. So yes, many of those numbers aren't actually the same as the actual number they represent, but if you typed them in in Python, you'd get that (slightly inaccurate) value without the math.
Why do the two list comprehensions evaluate differently?
0.1 is already inaccurate, as you've stated, so multiplying by it is not exactly equivalent to dividing by 10 (where at least both inputs are precise integers). Sometimes that inaccuracy means the result is not the same as dividing by 10; after all, you multiplied by "just over one tenth", not "one tenth".
The critical point here is that 10 is represented exactly in binary, whereas 0.1 is not. Dividing by 10 gets you the closest possible representation for each fraction; multiplying by the inexact conversion of 0.1 does not guarantee precision. Sometimes you get "close enough" to round off the result to a single decimal place, sometimes not.
Is that enough rationale?
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.
I try get ration of variable and get unexpected result. Can somebody explain this?
>>> value = 3.2
>>> ratios = value.as_integer_ratio()
>>> ratios
(3602879701896397, 1125899906842624)
>>> ratios[0] / ratios[1]
3.2
I using python 3.3
But I think that (16, 5) is much better solution
And why it correct for 2.5
>>> value = 2.5
>>> value.as_integer_ratio()
(5, 2)
Use the fractions module to simplify fractions:
>>> from fractions import Fraction
>>> Fraction(3.2)
Fraction(3602879701896397, 1125899906842624)
>>> Fraction(3.2).limit_denominator()
Fraction(16, 5)
From the Fraction.limit_denominator() function:
Finds and returns the closest Fraction to self that has denominator at most max_denominator. This method is useful for finding rational approximations to a given floating-point number
Floating point numbers are limited in precision and cannot represent many numbers exactly; what you see is a rounded representation, but the real number is:
>>> format(3.2, '.50f')
'3.20000000000000017763568394002504646778106689453125'
because a floating point number is represented as a sum of binary fractions; 1/5 can only be represented by adding up 1/8 + 1/16 + 1/128 + more binary fractions for increasing exponents of two.
It's not 16/5 because 3.2 isn't 3.2 exactly... it's a floating point rough approximation of it... eg: 3.20000000000000017764
While using the fractions module, it is better to provide a string instead of a float to avoid floating point representation issues.
For example, if you pass '3.2' instead of 3.2 you get your desired result:
In : fractions.Fraction('3.2')
Out: Fraction(16, 5)
If you already have the value stored in a variable, you can use string formatting as well.
In : value = 3.2
In : fractions.Fraction(f'{value:.2f}')
Out: Fraction(16, 5)
In Python 2.7.3, this is the current behavior:
>>> 8./9.
0.8888888888888888
>>> '%.1f' % (8./9.)
'0.9'
Same appears to be true for Decimals:
>>> from decimal import Decimal
>>> Decimal(8) / Decimal(9)
Decimal('0.8888888888888888888888888889')
>>> '%.1f' % (Decimal(8) / Decimal(9))
'0.9'
I would have expected truncation, however, it appears to round. So my options to truncating to the tenths place?
FYI I ask because my current solution seems hacky (but maybe its the best practice?) as it make a string of the result, finds the period and simply finds X digits after the period that I want.
You are looking for the math.floor() function instead:
>>> import math
>>> math.floor(8./9. * 10) / 10
0.8
So my options to truncating to the tenths place?
The Decimal.quantize() method rounds a number to a fixed exponent and it provides control over the rounding mode:
>>> from decimal import Decimal, ROUND_FLOOR
>>> Decimal('0.9876').quantize(Decimal('0.1'), rounding=ROUND_FLOOR)
Decimal('0.9')
Don't use math.floor on Decimal values because it first coerces them to a binary float introducing representation error and lost precision:
>>> x = Decimal('1.999999999999999999998')
>>> x.quantize(Decimal('0.1'), rounding=ROUND_FLOOR)
Decimal('1.9')
>>> math.floor(x * 10) / 10
2.0
Multiply by 10, then floor the value.
In some language:
float f = 1/3;
print(f) //Prints 0,3333333333
float q = Math.floor(f*10)/10
print(q) //Prints 0,3
What I need to do is use integer arithmetic to convert fractions into floating point numbers. The number of decimal places needed is specified as a variable DECIMALS. Each fraction is contained in a tuple of integers, for example (1, 3). The first item is the numerator and the second is the denominator. The tuples are contained in a list called fractions.
This is my code so far:
fractions = [(1,7), (2,3), (22,7), (7001,7), (9,3), (611951,611953), (1,11), (1,7689585)]
DECIMALS = 10**40 # 40 decimal places
for fraction in fractions:
x = DECIMALS*fraction[0]/fraction[1]
print x
When i run the code this is what i get:
1428571428571428571428571428571428571428
6666666666666666666666666666666666666666
31428571428571428571428571428571428571428
10001428571428571428571428571428571428571428
30000000000000000000000000000000000000000
9999967317751526669531810449495304377950
909090909090909090909090909090909090909
1300460297922449651053990559958697
The problem is that I need to format this into the correct decimal format. What I tried was
print "0.%.40d" % (x)
But of course this will only help me with the first 2 decimals; the rest will be wrong. I thought about dividing it up so I can calculate the fractions indivdually to make them easier to format, but I have no idea how to do this. The catch is that all the fractions need to be processed by the same code. I also want it to be rounded properly but thats not a big deal right now.
You mean something like this:
from fractions import Fraction
import decimal
decimal.getcontext().prec = 50
fractions = [(1,7), (2,3), (22,7), (7001,7), (9,3), (611951,611953), (1,11),(1,7689585)]
su=Fraction(0)
for i, t in enumerate(fractions,1):
f=Fraction(*t)
su+=Fraction(*t)
d=decimal.Decimal(f.numerator) / decimal.Decimal(f.denominator)
print '{} becomes {}'.format(t,f)
print '\tfloat of that is: {}'.format(float(f))
print '\tDecimal: {}'.format(d)
print '\t{} elements cum sum of the list: {}\n'.format(i,su)
Prints:
(1, 7) becomes 1/7
float of that is: 0.142857142857
Decimal: 0.14285714285714285714285714285714285714285714285714
1 elements cum sum of the list: 1/7
(2, 3) becomes 2/3
float of that is: 0.666666666667
Decimal: 0.66666666666666666666666666666666666666666666666667
2 elements cum sum of the list: 17/21
(22, 7) becomes 22/7
float of that is: 3.14285714286
Decimal: 3.1428571428571428571428571428571428571428571428571
3 elements cum sum of the list: 83/21
(7001, 7) becomes 7001/7
float of that is: 1000.14285714
Decimal: 1000.1428571428571428571428571428571428571428571429
4 elements cum sum of the list: 21086/21
(9, 3) becomes 3
float of that is: 3.0
Decimal: 3
5 elements cum sum of the list: 21149/21
(611951, 611953) becomes 611951/611953
float of that is: 0.999996731775
Decimal: 0.99999673177515266695318104494953043779505942449829
6 elements cum sum of the list: 12955044968/12851013
(1, 11) becomes 1/11
float of that is: 0.0909090909091
Decimal: 0.090909090909090909090909090909090909090909090909091
7 elements cum sum of the list: 142518345661/141361143
(1, 7689585) becomes 1/7689585
float of that is: 1.30046029792e-07
Decimal: 1.3004602979224496510539905599586973809379829990825E-7
8 elements cum sum of the list: 121767437017889092/120778724977295
The fraction module allows you to work with rational numbers (without converting to a float). Once you have put them into a Fraction class, you can do arithmetic with them (just like in grade school)
Like this:
>>> Fraction(1,3) + Fraction(3,4)
Fraction(13, 12)
>>> Fraction(1,3) + Fraction(1,6)
Fraction(1, 2)
>>> Fraction(1,2).numerator
1
>>> Fraction(1,2).denominator
2
The Fraction module is part of default Python distribution (since 2.6).
To convert that to a float, do float(Fraction(17,18)) for example or use Fraction.numerator and Fraction().denominator in a Decimal class variable for arbitrary precision conversion.
Like so:
>>> decimal.Decimal(su.numerator) / decimal.Decimal(su.denominator)
Decimal('1008.186144047968368606384293')
or:
>>> float(su.numerator) / su.denominator
1008.1861440479684
Avoid floating point numbers in the first place.
>>> decimal.getcontext().prec = 50
>>> [str((decimal.Decimal(x) / decimal.Decimal(y)).quantize(decimal.Decimal(10) ** -40)) for (x, y) in FRACTIONS]
['0.1428571428571428571428571428571428571429', '0.6666666666666666666666666666666666666667', '3.1428571428571428571428571428571428571429', '1000.1428571428571428571428571428571428571429', '3.0000000000000000000000000000000000000000', '0.9999967317751526669531810449495304377951', '0.0909090909090909090909090909090909090909', '1.300460297922449651053990559958697E-7']
If it strictly formatting, as implied by your question, you could, of course, do this with integers and strings alone:
def intF(n, d, l=40):
s=str(n*10**l / d)
if len(s) < l:
return '0.{:0>{width}}'.format(s,width=l)
if len(s) > l:
return s[0:len(s)-l]+'.'+s[len(s)-l:]
return '0.'+s
for f in [(1,7), (2,3), (22,7), (7001,7), (9,3),
(611951,611953), (1,11),(1,7689585)]:
print intF(*f)
print float(f[0]) / f[1]
print
Output:
0.1428571428571428571428571428571428571428
0.142857142857
0.6666666666666666666666666666666666666666
0.666666666667
3.1428571428571428571428571428571428571428
3.14285714286
1000.1428571428571428571428571428571428571428
1000.14285714
3.0000000000000000000000000000000000000000
3.0
0.9999967317751526669531810449495304377950
0.999996731775
0.0909090909090909090909090909090909090909
0.0909090909091
0.0000001300460297922449651053990559958697
1.30046029792e-07
The last digit will not correctly round and it will not handle edge cases ('inf', 'NaN', division by zero, etc) correctly.
Works in a pinch I suppose.
Why not use the batteries included though, like Decimal or Fraction?