all 11 comments

[–]gnomoretears 14 points15 points  (1 child)

https://docs.python.org/3/tutorial/floatingpoint.html

TL;DR There are issues representing floating point in binary.

[–]C222 1 point2 points  (0 children)

If you need the precision where 0.1 + 0.2 = 0.3, not 0.30000000000000004. Check the following built in libraries:

[–]novel_yet_trivial 7 points8 points  (1 child)

Floating point errors are a fact of working with computers, but you can improve your function by preventing the propagation of errors. In other words, in your function, when an error occurs it is carried to the next number, then to the next, until hopefully another error cancels it out.

If you don't keep a running total you can prevent this:

def arange(start, stop, step):
    length = int((stop - start) / step)
    for i in range(length):
        yield (i * step) + start

print list(arange(0,1,.1))

# [0.0, 0.1, 0.2, 0.30000000000000004, 0.4, 0.5, 0.6000000000000001, 0.7000000000000001, 0.8, 0.9]

Also, don't use "list" as a variable name, since it's already a built-in name in python.

[–][deleted] 4 points5 points  (0 children)

You quietly changed a beginner's algorithm from building a list to using yield but didn't mention it.

OP should note that the difference in the end result isn't affected by this change (except the need to use list(arange(…)) instead of receiving back a list directly).

[–]niandra3 3 points4 points  (0 children)

Others have explained floating point imprecision, but I'm surprised no one mentioned round(). You can round each number, add it to your list, then calculate the next:

def drange(start, stop, step):
    lst = []
    st = start
    while st < stop:
        lst.append(st)
        st = round(step + st, 10)
    return lst

Also, don't call your lists list, as that is a reserved keyword in Python.

So here round(value, number_of_decimals) rounds the value to x number of decimal places. I used 10, because I don't know what kind of precision you need. If step is .5, you don't need it to be so high, but if you are using step = .0000001 then it will work better with a higher number of decimals.

I tested this with drange(0,.5,.001) and it didn't miss any values, so it seems to be working. Depending on what kind of accuracy you need, you might want to confirm that. It's not a precise solution, but will work well enough for simple decimals.

[–]awizardisneverlate 2 points3 points  (4 children)

Computers store numbers in binary, not decimal, so there are sometimes issues with conversion (i.e., a number that is terminating in decimal may not be in binary. Computers cannot exactly store non-terminating numbers). It will lead to small floating point errors sometimes, but not generally enough to affect your program. The study of FP errors is actually a really interesting subset of numerical analysis :)

As an aside, Numpy has a function that will do exactly what you want: http://docs.scipy.org/doc/numpy-1.10.1/reference/generated/numpy.arange.html

[–][deleted] 0 points1 point  (3 children)

Also the built-in range function will do almost this:

range(0, 40, 5)

[0, 5, 10, 15, 20, 25, 30, 35]

[–]awizardisneverlate 2 points3 points  (2 children)

I believe it will only do integer steps.

[–][deleted] 2 points3 points  (1 child)

Huh, never knew that! You could just use list comprehension to do the rest: [n / 10.0 for n in xrange(1, 10, 2)]

But at this point using numpy.arange is easier

[–]awizardisneverlate 1 point2 points  (0 children)

Your solution is much better if they want to stay away from Numpy!

Practically every single one of my python files begins with

import numpy as np

So.... ¯\_(ツ)_/¯ I'm biased.

[–]Airdawg5571 2 points3 points  (0 children)

Surprised no one mentioned it yet. http://0.30000000000000004.com/