Reputation: 13
Python, version 3.5
I have the following code:
def drange(start, stop, step):
r = start
while r < stop:
yield r
r += step
def my_func():
whole_nums = [float(num) for num in range(0, 100)]
for x in drange(0.0, 100.01, 0.01):
if str(x).split('.')[1] == '0':
x = str(x).split('.')[0]
else:
x = round(x, 2)
print(str(x))
time.sleep(0.2)
the drange
function loops through given numbers, with a given incremental and yields the numbers. The issue is that I want 0, 1, 2, 3 etc, but it will yield 0.0, 1.0, and so on.
I figured that since I knew the format of those wrong numbers (all other numbers in-between like 0.01, all the way to 99.99 I want) I could just do:
str(theNumber).split('.')[1]
(as seen in my_func
) and if the output was '0'
then I would know I had a 0.0, 1.0 etc and could fetch the output I wanted with str(theNumber).split('.')[0]
and of course convert it to an int or whatever.
But! When I execute my_func
, then the .split()
solution only works on 0.0, when it reaches 1.0 it just skips the if
statement and goes into the else
, printing out 1.0 instead of 1.
The code should run fine without importing anything so feel free to give it a whirl.
Okay, it seems my approach is fundamentally flawed (see @danils answer) so my question is now: how can I generate the numbers ranging from 0 to 100, in increments of 0.01 while ensuring whole numbers are whole. so
0, 0.01, ..., 1, 1.01, 1.1, 1.02, ... 99.5, 99.51, ..., 100
Upvotes: 1
Views: 1116
Reputation: 2781
For a version not involving numpy:
import time
def drange(start, stop, step):
r = start
while r < stop:
yield r
r += step
def my_func():
whole_nums = [float(num) for num in range(0, 100)]
for x in drange(0.0, 100.01, 0.01):
x = float(int(x * 100))/100
if x.is_integer():
print(int(x))
else:
print (x)
time.sleep(0.01)
if __name__ == "__main__":
my_func()
Admittedly the truncating function is cheesy.
Upvotes: 0
Reputation: 402393
You should never rely on the string representation of floating point integers, because they seldom have an exact representation (approximations lead to precision inaccuracies which throw spanners in the works).
You don't need str(x)
to print
, because print
automatically does that for you.
Furthermore, it seems your drange
function introduces floating point inaccuracies when generating the next values in step. You could instead use np.arange
for the range, it supports decimal steps:
import numpy as np
out = np.arange(0.0, 100.01, 0.01)
print(out)
array([ 0.00000000e+00, 1.00000000e-02, 2.00000000e-02, ...,
9.99800000e+01, 9.99900000e+01, 1.00000000e+02])
Note: If you don't have numpy
, you can install it using pip install numpy
.
And lastly, you could use float.is_integer
to check if a float is a whole number.
for x in np.arange(0.0, 100.01, 0.01):
if x.is_integer():
x = int(x)
else:
x = round(x, 2)
print(x)
This gives you:
0
0.01
0.02
0.03
0.04
0.05
0.06
0.07
0.08
0.09
0.1
...
Upvotes: 2
Reputation: 30453
It's because of floating-point arithmetic. It's not exactly 1.0
what you get, more like 1.0000000000000007
. So str(x).split('.')[1] == '0000000000000007'
.
> import numpy as np
> np.arange(0.0, 100.0, 0.1)
array([ 0. , 0.1, 0.2, ..., 99.8, 99.9, 100. ])
Upvotes: 0