Python float to ratio - python

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

Fractions from provided numerator [duplicate]

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)

How to deal with rounding errors in python math.ceil

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.

Python sum of big float numbers

i am trying to add some float values in python 3 (never tested in 2) and i get some odd results, the only varying factor being the order of the elements in the summation.
a = [-1e30, 1e30, 1, 3]
print(sum(a)) # return 4.0
a = [-1e30, 1, 3, 1e30]
print(sum(a)) # return 0.0
Can anyone please tell me what did i miss here?
Thanks in advance!
When you're doing sums of sequences of floating point numbers, you want to use math.fsum:
>>> a = [-1e30, 1e30, 1, 3]
>>> math.fsum(a)
4.0
>>> a = [-1e30, 1, 3, 1e30]
>>> math.fsum(a)
4.0
Using the sum builtin is not going to get you good answers for very large (or small) floating point numbers because of the inherent precision problems. You can get a pretty good view of the gory details at What Every Computer Scientist Should Know About Floating-Point Arithmetic.
Floating point numbers have a 53-bit coefficient ("the number before the e").
10**30 is way bigger than 2**53, so adding 4 to that number is lost in the limits of precision for floating point numbers.
>>> 2**53
9007199254740992
>>> 10**30
1000000000000000000000000000000
>>> float(2**53)
9007199254740992.0
>>> float(2**53) + 1
9007199254740992.0

How to properly truncate a float/decimal to a specific place after the decimal in python?

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

python floating number [duplicate]

This question already has answers here:
Why does floating-point arithmetic not give exact results when adding decimal fractions?
(31 answers)
Closed 3 years ago.
i am kind of confused why python add some additional decimal number in this case, please help to explain
>>> mylist = ["list item 1", 2, 3.14]
>>> print mylist ['list item 1', 2, 3.1400000000000001]
Floating point numbers are an approximation, they cannot store decimal numbers exactly. Because they try to represent a very large range of numbers in only 64 bits, they must approximate to some extent.
It is very important to be aware of this, because it results in some weird side-effects. For example, you might very reasonably think that the sum of ten lots of 0.1 would be 1.0. While this seems logical, it is also wrong when it comes to floating point:
>>> f = 0.0
>>> for _ in range (10):
... f += 0.1
...
>>> print f == 1.0
False
>>> f
0.99999999999999989
>>> str(f)
1.0
You might think that n / m * m == n. Once again, floating-point world disagrees:
>>> (1.0 / 103.0) * 103.0
0.99999999999999989
Or perhaps just as strangely, one might think that for all n, n + 1 != n. In floating point land, numbers just don't work like this:
>>> 10.0**200
9.9999999999999997e+199
>>> 10.0**200 == 10.0**200 + 1
True
# How much do we have to add to 10.0**200 before its
# floating point representation changes?
>>> 10.0**200 == 10.0**200 + 10.0**183
True
>>> 10.0**200 == 10.0**200 + 10.0**184
False
See What every computer scientist should know about floating point numbers for an excellent summary of the issues.
If you need exact decimal representation, check out the decimal module, part of the python standard library since 2.4. It allows you to specify the number of significant figures. The downside is, it is much slower than floating point, because floating point operations are implemented in hardware whereas decimal operations happen purely in software. It also has its own imprecision issues, but if you need exact representation of decimal numbers (e.g. for a financial application) it's ideal.
For example:
>>> 3.14
3.1400000000000001
>>> import decimal
>>> decimal.Decimal('3.14')
>>> print decimal.Decimal('3.14')
3.14
# change the precision:
>>> decimal.getcontext().prec = 6
>>> decimal.Decimal(1) / decimal.Decimal(7)
Decimal('0.142857')
>>> decimal.getcontext().prec = 28
>>> decimal.Decimal(1) / decimal.Decimal(7)
Decimal('0.1428571428571428571428571429')
It is worthwhile to note that Python 3.1 has a new floating point output routine that rounds this in the expected manner (it has also been backported to Python 2.7):
Python 3.1 (r31:73572, Aug 15 2009, 17:12:41)
[GCC 4.0.1 (Apple Computer, Inc. build 5367)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> a = [3.14]
>>> print(a)
[3.14]
From the What's New in Python 3.1 document:
Python now uses David Gay’s algorithm for finding the shortest floating point representation that doesn’t change its value. This should help mitigate some of the confusion surrounding binary floating point numbers.
The significance is easily seen with a number like 1.1 which does not have an exact equivalent in binary floating point. Since there is no exact equivalent, an expression like float('1.1') evaluates to the nearest representable value which is 0x1.199999999999ap+0 in hex or 1.100000000000000088817841970012523233890533447265625 in decimal. That nearest value was and still is used in subsequent floating point calculations.
As mentioned before, it's all about floating points being an approximation.
If you want exactness you can use a decimal (which is a precise representation):
http://docs.python.org/library/decimal.html
a = [1.5, 1.49999]
a
[1.5, 1.4999899999999999]
from decimal import Decimal
b = [1.5, Decimal('1.4999')]
b
[1.5, Decimal('1.4999')]
We can fix it by this command:
>>> x = 1.2 - 1.0
>>> x
0.19999999999999996
>>> y = float(str(x))
>>> y
0.2
I add an answer from #mark

Categories

Resources