Why does this While loop terminate? - python

x=1.0
i=1
while(1.0+x>1.0):
x=x/2
i=i+1
print i
Follow up question, why is the value of i=54?
My thinking was that the loop would not end as the value of (1.0+x) will always stay greater than 1.0. But when running the code, that's not the case.

Due to the inaccuracy of floating point, there will always come a time when the value of x is so small that Python can't store its value, and it essentially becomes 0. It takes 54 iterations (53, actually) to get to that stage, which is why i is 54.
For example,
>>> 1e-1000
0.0

Why 54? -- Actually it is 53, because it was before you increment it
>>> 2.**-54
5.551115123125783e-17
>>> 2.**-53
1.1102230246251565e-16
>>> 2.**-52
2.220446049250313e-16
>>> sys.float_info.epsilon
2.220446049250313e-16
if you add something so small to 1, it will be still 1.

When dealing with floats or floating point numbers, you will encounter the notorious Floating Point Epsilon:
In your case, this takes 54 iterations to get below that threshold (since the default floating point type in Python is single precision, and the floating point epsilon for single precision is:
def machineEpsilon(func=float):
machine_epsilon = func(1)
while func(1)+func(machine_epsilon) != func(1):
machine_epsilon_last = machine_epsilon
machine_epsilon = func(machine_epsilon) / func(2)
return machine_epsilon_last
Hence:
In [2]: machineEpsilon(float)
Out[2]: 2.2204460492503131e-16
Where does the 53 iterations come from?
From this line in your code:
x=x/2
Which assigns the current value of x to x/2 meaning that on the 53th iteration, it became:
1.11022302463e-16
Which is less than the floating point epsilon.

As has been pointed out - it's because of the accuracy of floats. If you wanted to overcome this "limitation" you can use Python's fractions module, eg:
from fractions import Fraction as F
x = F(1, 1)
i=1
while(F(1, 1)+x>1.0):
print i, x
x = F(1, x.denominator * 2)
i=i+1
print i
(NB: This will continue until interrupted)

Related

What is the smallest number that when added to the largest floating point number smaller than infinity results in infinity?

The largest number in python is supposed to be:
l=2**(1023)*(2-2**(-52))
1.7976931348623157e+308
This can be verified with the instruction:
sys.float_info.max
1.7976931348623157e+308
However, see the following
1.0000000000000000000000000001*l
1.7976931348623157e+308
and now:
1.00006*l
inf
What is going on? For which x happened that (1+x-ε) = 1.7976931348623157e+308 and (1+x) = inf?
Update:
I believe the largest number that triggers infinity in python is between
sys.float_info.max + 0.5*epsilon and sys.float_info.max + 0.51*epsilon
with epsilon = $2^{-52}$ being the epsilon of the
computer.
See this:
l = sys_float_info.max
(1+0.5*epsilon)*l
1.7976931348623157e+308
(1+0.51*epsilon)*l
inf
In the first case you're actually multiplying by exactly 1:
>>> 1.0000000000000000000000000001
1.0
Binary floating-point is not WYSIWYG - it's subject to rounding back to machine precision at all stages.
Continuing the above:
>>> 1.0000000000000001
1.0
>>> 1.000000000000001
1.000000000000001
So 1.000000000000001 is the smallest literal of this form that doesn't round to exactly 1.0. And then:
>>> sys.float_info.max * 1.000000000000001
inf
Note that it's enough to multiply by the smallest representable float greater than 1, which is a little smaller than that literal's rounded value:
>>> import math
>>> math.nextafter(1.0, 100) * sys.float_info.max
inf
>>> 1.000000000000001 > math.nextafter(1.0, 100)
True
What about addition?
While examples show multiplication, the title of the question asks about addition. So let's do that too. Learn about nextafter() and ulp(), which are the right tools with which to approach questions like this. First, an easy computational way to find the largest finite representable float:
>>> import math
>>> big = math.nextafter(math.inf, 0)
>>> big
1.7976931348623157e+308
Now set lastbit to the value of its least-significant bit:
>>> lastbit = math.ulp(big)
>>> lastbit
1.99584030953472e+292
Certainly if we add that to big it will overflow to infinity. And so it does:
>>> big + lastbit
inf
However, adding even half that will also overflow, due to "to nearest/even" rounding resolving the halfway tie "up":
>>> big + lastbit / 2.0
inf
Will anything smaller work? No. Anything smaller will just be thrown away by rounding "down". Here we try adding the next smallest representable float, and see that it has no effect:
>>> big + math.nextafter(lastbit / 2.0, 0)
1.7976931348623157e+308

Exact Value after Floating point not rounding up [duplicate]

I want to remove digits from a float to have a fixed number of digits after the dot, like:
1.923328437452 → 1.923
I need to output as a string to another function, not print.
Also I want to ignore the lost digits, not round them.
round(1.923328437452, 3)
See Python's documentation on the standard types. You'll need to scroll down a bit to get to the round function. Essentially the second number says how many decimal places to round it to.
First, the function, for those who just want some copy-and-paste code:
def truncate(f, n):
'''Truncates/pads a float f to n decimal places without rounding'''
s = '{}'.format(f)
if 'e' in s or 'E' in s:
return '{0:.{1}f}'.format(f, n)
i, p, d = s.partition('.')
return '.'.join([i, (d+'0'*n)[:n]])
This is valid in Python 2.7 and 3.1+. For older versions, it's not possible to get the same "intelligent rounding" effect (at least, not without a lot of complicated code), but rounding to 12 decimal places before truncation will work much of the time:
def truncate(f, n):
'''Truncates/pads a float f to n decimal places without rounding'''
s = '%.12f' % f
i, p, d = s.partition('.')
return '.'.join([i, (d+'0'*n)[:n]])
Explanation
The core of the underlying method is to convert the value to a string at full precision and then just chop off everything beyond the desired number of characters. The latter step is easy; it can be done either with string manipulation
i, p, d = s.partition('.')
'.'.join([i, (d+'0'*n)[:n]])
or the decimal module
str(Decimal(s).quantize(Decimal((0, (1,), -n)), rounding=ROUND_DOWN))
The first step, converting to a string, is quite difficult because there are some pairs of floating point literals (i.e. what you write in the source code) which both produce the same binary representation and yet should be truncated differently. For example, consider 0.3 and 0.29999999999999998. If you write 0.3 in a Python program, the compiler encodes it using the IEEE floating-point format into the sequence of bits (assuming a 64-bit float)
0011111111010011001100110011001100110011001100110011001100110011
This is the closest value to 0.3 that can accurately be represented as an IEEE float. But if you write 0.29999999999999998 in a Python program, the compiler translates it into exactly the same value. In one case, you meant it to be truncated (to one digit) as 0.3, whereas in the other case you meant it to be truncated as 0.2, but Python can only give one answer. This is a fundamental limitation of Python, or indeed any programming language without lazy evaluation. The truncation function only has access to the binary value stored in the computer's memory, not the string you actually typed into the source code.1
If you decode the sequence of bits back into a decimal number, again using the IEEE 64-bit floating-point format, you get
0.2999999999999999888977697537484345957637...
so a naive implementation would come up with 0.2 even though that's probably not what you want. For more on floating-point representation error, see the Python tutorial.
It's very rare to be working with a floating-point value that is so close to a round number and yet is intentionally not equal to that round number. So when truncating, it probably makes sense to choose the "nicest" decimal representation out of all that could correspond to the value in memory. Python 2.7 and up (but not 3.0) includes a sophisticated algorithm to do just that, which we can access through the default string formatting operation.
'{}'.format(f)
The only caveat is that this acts like a g format specification, in the sense that it uses exponential notation (1.23e+4) if the number is large or small enough. So the method has to catch this case and handle it differently. There are a few cases where using an f format specification instead causes a problem, such as trying to truncate 3e-10 to 28 digits of precision (it produces 0.0000000002999999999999999980), and I'm not yet sure how best to handle those.
If you actually are working with floats that are very close to round numbers but intentionally not equal to them (like 0.29999999999999998 or 99.959999999999994), this will produce some false positives, i.e. it'll round numbers that you didn't want rounded. In that case the solution is to specify a fixed precision.
'{0:.{1}f}'.format(f, sys.float_info.dig + n + 2)
The number of digits of precision to use here doesn't really matter, it only needs to be large enough to ensure that any rounding performed in the string conversion doesn't "bump up" the value to its nice decimal representation. I think sys.float_info.dig + n + 2 may be enough in all cases, but if not that 2 might have to be increased, and it doesn't hurt to do so.
In earlier versions of Python (up to 2.6, or 3.0), the floating point number formatting was a lot more crude, and would regularly produce things like
>>> 1.1
1.1000000000000001
If this is your situation, if you do want to use "nice" decimal representations for truncation, all you can do (as far as I know) is pick some number of digits, less than the full precision representable by a float, and round the number to that many digits before truncating it. A typical choice is 12,
'%.12f' % f
but you can adjust this to suit the numbers you're using.
1Well... I lied. Technically, you can instruct Python to re-parse its own source code and extract the part corresponding to the first argument you pass to the truncation function. If that argument is a floating-point literal, you can just cut it off a certain number of places after the decimal point and return that. However this strategy doesn't work if the argument is a variable, which makes it fairly useless. The following is presented for entertainment value only:
def trunc_introspect(f, n):
'''Truncates/pads the float f to n decimal places by looking at the caller's source code'''
current_frame = None
caller_frame = None
s = inspect.stack()
try:
current_frame = s[0]
caller_frame = s[1]
gen = tokenize.tokenize(io.BytesIO(caller_frame[4][caller_frame[5]].encode('utf-8')).readline)
for token_type, token_string, _, _, _ in gen:
if token_type == tokenize.NAME and token_string == current_frame[3]:
next(gen) # left parenthesis
token_type, token_string, _, _, _ = next(gen) # float literal
if token_type == tokenize.NUMBER:
try:
cut_point = token_string.index('.') + n + 1
except ValueError: # no decimal in string
return token_string + '.' + '0' * n
else:
if len(token_string) < cut_point:
token_string += '0' * (cut_point - len(token_string))
return token_string[:cut_point]
else:
raise ValueError('Unable to find floating-point literal (this probably means you called {} with a variable)'.format(current_frame[3]))
break
finally:
del s, current_frame, caller_frame
Generalizing this to handle the case where you pass in a variable seems like a lost cause, since you'd have to trace backwards through the program's execution until you find the floating-point literal which gave the variable its value. If there even is one. Most variables will be initialized from user input or mathematical expressions, in which case the binary representation is all there is.
The result of round is a float, so watch out (example is from Python 2.6):
>>> round(1.923328437452, 3)
1.923
>>> round(1.23456, 3)
1.2350000000000001
You will be better off when using a formatted string:
>>> "%.3f" % 1.923328437452
'1.923'
>>> "%.3f" % 1.23456
'1.235'
n = 1.923328437452
str(n)[:4]
At my Python 2.7 prompt:
>>> int(1.923328437452 * 1000)/1000.0
1.923
The truely pythonic way of doing it is
from decimal import *
with localcontext() as ctx:
ctx.rounding = ROUND_DOWN
print Decimal('1.923328437452').quantize(Decimal('0.001'))
or shorter:
from decimal import Decimal as D, ROUND_DOWN
D('1.923328437452').quantize(D('0.001'), rounding=ROUND_DOWN)
Update
Usually the problem is not in truncating floats itself, but in the improper usage of float numbers before rounding.
For example: int(0.7*3*100)/100 == 2.09.
If you are forced to use floats (say, you're accelerating your code with numba), it's better to use cents as "internal representation" of prices: (70*3 == 210) and multiply/divide the inputs/outputs.
Simple python script -
n = 1.923328437452
n = float(int(n * 1000))
n /=1000
def trunc(num, digits):
sp = str(num).split('.')
return '.'.join([sp[0], sp[1][:digits]])
This should work. It should give you the truncation you are looking for.
So many of the answers given for this question are just completely wrong. They either round up floats (rather than truncate) or do not work for all cases.
This is the top Google result when I search for 'Python truncate float', a concept which is really straightforward, and which deserves better answers. I agree with Hatchkins that using the decimal module is the pythonic way of doing this, so I give here a function which I think answers the question correctly, and which works as expected for all cases.
As a side-note, fractional values, in general, cannot be represented exactly by binary floating point variables (see here for a discussion of this), which is why my function returns a string.
from decimal import Decimal, localcontext, ROUND_DOWN
def truncate(number, places):
if not isinstance(places, int):
raise ValueError("Decimal places must be an integer.")
if places < 1:
raise ValueError("Decimal places must be at least 1.")
# If you want to truncate to 0 decimal places, just do int(number).
with localcontext() as context:
context.rounding = ROUND_DOWN
exponent = Decimal(str(10 ** - places))
return Decimal(str(number)).quantize(exponent).to_eng_string()
>>> from math import floor
>>> floor((1.23658945) * 10**4) / 10**4
1.2365
# divide and multiply by 10**number of desired digits
If you fancy some mathemagic, this works for +ve numbers:
>>> v = 1.923328437452
>>> v - v % 1e-3
1.923
I did something like this:
from math import trunc
def truncate(number, decimals=0):
if decimals < 0:
raise ValueError('truncate received an invalid value of decimals ({})'.format(decimals))
elif decimals == 0:
return trunc(number)
else:
factor = float(10**decimals)
return trunc(number*factor)/factor
You can do:
def truncate(f, n):
return math.floor(f * 10 ** n) / 10 ** n
testing:
>>> f=1.923328437452
>>> [truncate(f, n) for n in range(5)]
[1.0, 1.9, 1.92, 1.923, 1.9233]
Just wanted to mention that the old "make round() with floor()" trick of
round(f) = floor(f+0.5)
can be turned around to make floor() from round()
floor(f) = round(f-0.5)
Although both these rules break around negative numbers, so using it is less than ideal:
def trunc(f, n):
if f > 0:
return "%.*f" % (n, (f - 0.5*10**-n))
elif f == 0:
return "%.*f" % (n, f)
elif f < 0:
return "%.*f" % (n, (f + 0.5*10**-n))
def precision(value, precision):
"""
param: value: takes a float
param: precision: int, number of decimal places
returns a float
"""
x = 10.0**precision
num = int(value * x)/ x
return num
precision(1.923328437452, 3)
1.923
Short and easy variant
def truncate_float(value, digits_after_point=2):
pow_10 = 10 ** digits_after_point
return (float(int(value * pow_10))) / pow_10
>>> truncate_float(1.14333, 2)
>>> 1.14
>>> truncate_float(1.14777, 2)
>>> 1.14
>>> truncate_float(1.14777, 4)
>>> 1.1477
When using a pandas df this worked for me
import math
def truncate(number, digits) -> float:
stepper = 10.0 ** digits
return math.trunc(stepper * number) / stepper
df['trunc'] = df['float_val'].apply(lambda x: truncate(x,1))
df['trunc']=df['trunc'].map('{:.1f}'.format)
int(16.5);
this will give an integer value of 16, i.e. trunc, won't be able to specify decimals, but guess you can do that by
import math;
def trunc(invalue, digits):
return int(invalue*math.pow(10,digits))/math.pow(10,digits);
Here is an easy way:
def truncate(num, res=3):
return (floor(num*pow(10, res)+0.5))/pow(10, res)
for num = 1.923328437452, this outputs 1.923
def trunc(f,n):
return ('%.16f' % f)[:(n-16)]
A general and simple function to use:
def truncate_float(number, length):
"""Truncate float numbers, up to the number specified
in length that must be an integer"""
number = number * pow(10, length)
number = int(number)
number = float(number)
number /= pow(10, length)
return number
There is an easy workaround in python 3. Where to cut I defined with an help variable decPlace to make it easy to adapt.
f = 1.12345
decPlace= 4
f_cut = int(f * 10**decPlace) /10**decPlace
Output:
f = 1.1234
Hope it helps.
Most answers are way too complicated in my opinion, how about this?
digits = 2 # Specify how many digits you want
fnum = '122.485221'
truncated_float = float(fnum[:fnum.find('.') + digits + 1])
>>> 122.48
Simply scanning for the index of '.' and truncate as desired (no rounding).
Convert string to float as final step.
Or in your case if you get a float as input and want a string as output:
fnum = str(122.485221) # convert float to string first
truncated_float = fnum[:fnum.find('.') + digits + 1] # string output
I think a better version would be just to find the index of decimal point . and then to take the string slice accordingly:
def truncate(number, n_digits:int=1)->float:
'''
:param number: real number ℝ
:param n_digits: Maximum number of digits after the decimal point after truncation
:return: truncated floating point number with at least one digit after decimal point
'''
decimalIndex = str(number).find('.')
if decimalIndex == -1:
return float(number)
else:
return float(str(number)[:decimalIndex+n_digits+1])
int(1.923328437452 * 1000) / 1000
>>> 1.923
int(1.9239 * 1000) / 1000
>>> 1.923
By multiplying the number by 1000 (10 ^ 3 for 3 digits) we shift the decimal point 3 places to the right and get 1923.3284374520001. When we convert that to an int the fractional part 3284374520001 will be discarded. Then we undo the shifting of the decimal point again by dividing by 1000 which returns 1.923.
use numpy.round
import numpy as np
precision = 3
floats = [1.123123123, 2.321321321321]
new_float = np.round(floats, precision)
Something simple enough to fit in a list-comprehension, with no libraries or other external dependencies. For Python >=3.6, it's very simple to write with f-strings.
The idea is to let the string-conversion do the rounding to one more place than you need and then chop off the last digit.
>>> nout = 3 # desired number of digits in output
>>> [f'{x:.{nout+1}f}'[:-1] for x in [2/3, 4/5, 8/9, 9/8, 5/4, 3/2]]
['0.666', '0.800', '0.888', '1.125', '1.250', '1.500']
Of course, there is rounding happening here (namely for the fourth digit), but rounding at some point is unvoidable. In case the transition between truncation and rounding is relevant, here's a slightly better example:
>>> nacc = 6 # desired accuracy (maximum 15!)
>>> nout = 3 # desired number of digits in output
>>> [f'{x:.{nacc}f}'[:-(nacc-nout)] for x in [2.9999, 2.99999, 2.999999, 2.9999999]]
>>> ['2.999', '2.999', '2.999', '3.000']
Bonus: removing zeros on the right
>>> nout = 3 # desired number of digits in output
>>> [f'{x:.{nout+1}f}'[:-1].rstrip('0') for x in [2/3, 4/5, 8/9, 9/8, 5/4, 3/2]]
['0.666', '0.8', '0.888', '1.125', '1.25', '1.5']
The core idea given here seems to me to be the best approach for this problem.
Unfortunately, it has received less votes while the later answer that has more votes is not complete (as observed in the comments). Hopefully, the implementation below provides a short and complete solution for truncation.
def trunc(num, digits):
l = str(float(num)).split('.')
digits = min(len(l[1]), digits)
return l[0] + '.' + l[1][:digits]
which should take care of all corner cases found here and here.
Am also a python newbie and after making use of some bits and pieces here, I offer my two cents
print str(int(time.time()))+str(datetime.now().microsecond)[:3]
str(int(time.time())) will take the time epoch as int and convert it to string and join with...
str(datetime.now().microsecond)[:3] which returns the microseconds only, convert to string and truncate to first 3 chars
# value value to be truncated
# n number of values after decimal
value = 0.999782
n = 3
float(int(value*1en))*1e-n

How are data types interpreted, calculated, and/or stored?

In python, suppose the code is:
import.math
a = math.sqrt(2.0)
if a * a == 2.0:
x = 2
else:
x = 1
This is a variant of "Floating Point Numbers are Approximations -- Not Exact".
Mathematically speaking, you are correct that sqrt(2) * sqrt(2) == 2. But sqrt(2) can not be exactly represented as a native datatype (read: floating point number). (Heck, the sqrt(2) is actually guaranteed to be an infinite decimal!). It can get really close, but not exact:
>>> import math
>>> math.sqrt(2)
1.4142135623730951
>>> math.sqrt(2) * math.sqrt(2)
2.0000000000000004
Note the result is, in fact, not exactly 2.
If you want the x = 2 branch to execute, you will need to use an epsilon value of "is the result close enough?":
epsilon = 1e-6 # 0.000001
if abs(2.0 - a*a) < epsilon:
x = 2
else:
x = 1
Numbers with decimals are stored as floating point numbers and they can only be an approximation to the real number in some cases.
So your comparison needs to be not "are these two numbers exactly equal (==)" but "are they sufficiently close as to be considered equal".
Fortunately, in the math library, there's a function to do that conveniently. Using isClose(), you can compare with a defined tolerance. The function isn't too complicated, you could do it yourself.
math.isclose(a*a, 2, abs_tol=0.0001)
>> True

Measuring the accuracy of floating point results to N decimal places

I'm testing some implementations of Pi in python (64-bit OS) and am interested in measuring how accurate the answer is (how many decimal places were correct?) for increasing iterations. I don't wish to compare more than 15 decimal places because beyond that the floating point representation itself is inaccurate.
E.g. for a low iteration count, the answer I got is
>>> x
3.140638056205993
I wish to compare to math.pi
>>> math.pi
3.141592653589793
For the above I wish my answer to be 3 (3rd decimal is wrong)
The way I've done it is:
>>> p = str('%.51f' % math.pi)
>>> q = str('%.51f' % x)
>>> for i,(a,b) in enumerate(zip(p,q)):
... if a != b:
... break
The above looks clumsy to me, i.e. converting floats to strings and then comparing character by character, is there a better way of doing this, say more Pythonic or that uses the raw float values themselves?
Btw I found math.frexp, can this be used to do this?
>>> math.frexp(x)
(0.7851595140514982, 2)
You can compute the logarithm of the difference between the two
>>> val = 3.140638056205993
>>> epsilon = abs(val - math.pi)
>>> abs(int(math.log(epsilon, 10))) + 1
3
Essentially, you're finding out which power of 10 does it take to equal the difference between the two numbers. This only works if the difference between the two numbers is less than 1.

testing for numeric equality when variable is modified inside loop

I am new to python and I was writing something like:
t = 0.
while t<4.9:
t = t + 0.1
if t == 1.:
... do something ...
I noticed that the if statement was never being executed. So I modified the code to look like this:
''' Case a'''
t = 0.
while t<4.9:
t = t + 0.1
print(t)
print(t == 5.)
When I run this I get:
>>> ================================ RESTART ================================
>>>
5.0
False
This was a surprise because I expected the comparison to test as True. Then, I tried the following two cases:
''' Case b'''
t = 0
while t<5:
t = t + 1
print(t)
print(t == 5)
''' Case c'''
t = 0.
while t<5:
t = t + 0.5
print(t)
print(t == 5)
When I run the last 2 cases (b and c) the comparison in the final statement tests as True. I do not understand why it is so or why it seems that the behavior is not consistent. What am I doing wrong?
The problem is that binary floating point arithmetic is not precise so you will get small errors in the calculations. In particular the number 0.1 has no exact binary representation. When you calculate using floating point numbers the very small errors cause the result to be slightly incorrect from what you might expect and that makes the equality test fail.
This small error might not be visible when printing the float with the default string representation. Try using repr instead, as this gives a slightly more accurate representation of the number (but still not 100% accurate):
>>> print(repr(t))
4.999999999999998
>>> print(t == 5.)
False
To get an accurate string representation of a float you can use the format method:
>>> print '{0:.60f}'.format(t)
4.999999999999998223643160599749535322189331054687500000000000
>>> print '{0:.60f}'.format(0.1)
0.100000000000000005551115123125782702118158340454101562500000
A general rule with floating point arithmetic is to never make equality comparisons.
The reason why it works when you used 0.5 is because 0.5 does have an exact representation as a binary floating point number so you don't see any problem in that case. Similarly it would work for 0.25 or 0.125.
If you need precise calculations you can use a decimal type instead.
from decimal import Decimal
step = Decimal('0.1')
t = Decimal(0)
while t < Decimal(5):
t += step
print(t)
print(t == Decimal(5))
Result:
5.0
True
NEVER try to test floats for equality.
Floats are often not exactly what you inputted them to be.
In [43]: .1
Out[43]: 0.10000000000000001
So it's much safer to only test floats with inequalities.
If you need to test if two floats are nearly equal, use a utility function like near:
def near(a,b,rtol=1e-5,atol=1e-8):
try:
return abs(a-b)<(atol+rtol*abs(b))
except TypeError:
return False
The rtol parameter allows you to specify relative tolerance. (abs(a-b)/abs(b)) < rtol
The atol parameter allows you to specify absolute tolerance. abs(a-b) < atol
So for example, you could write
t = 0.
while t<4.9:
t = t + 0.1
if near(t,1.):
print('Hiya')

Categories