1

I want to get a list of numbers such as [-2.3,-2.2,...,0.2] in Python. However, the attempt:

[float(i)/10 for i in range(-23,2)] 

will return [-2.2999999999999998, -2.2000000000000002, -2.1000000000000001, -2.0, -1.8999999999999999, -1.8, -1.7, -1.6000000000000001, -1.5, -1.3999999999999999, -1.3, -1.2, -1.1000000000000001, -1.0, -0.90000000000000002, -0.80000000000000004, -0.69999999999999996, -0.59999999999999998, -0.5, -0.40000000000000002, -0.29999999999999999, -0.20000000000000001, -0.10000000000000001, 0.0]

How do I fix this?

Round doesn't work:

>>> round(float(-23)/float(10),3)
-2.2999999999999998

It's for an eventual write to CSV or pass to another language through main method.

user2763361
  • 3,499
  • 9
  • 37
  • 74
  • 2
    What Python are you using, and on which operating system? – Lennart Regebro Nov 20 '13 at 07:11
  • 1
    If you actually need more precision than one part in a million billions, then read http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html. – Tadeusz A. Kadłubowski Nov 20 '13 at 07:15
  • Most languages have a decimal type for exact decimal numbers. It looks like python has one too: http://docs.python.org/2/library/decimal.html –  Nov 20 '13 at 07:20
  • @jdv-JandeVaan The Decimal in Python won't actually make much difference in this case though, as it's a float, but you set a precision. So it's a sort of "repr()" hack. But yes, using Decimal is a good idea if you want exact decimals. – Lennart Regebro Nov 20 '13 at 07:31
  • 1
    OK, since you don't answer: I think you are using Python 2.6 or earlier. All later versions of Python has changed the way floats are printed/represented as to provide less confusion for newbies in this case. You should probably upgrade to 2.7. – Lennart Regebro Nov 20 '13 at 07:37
  • This is essentially a duplicate of [Why can't decimal numbers be represented exactly in binary?](http://stackoverflow.com/questions/1089018/why-cant-decimal-numbers-be-represented-exactly-in-binary) in that as soon as you understand that question, you have the answer to this one as well. – Lennart Regebro Nov 20 '13 at 08:37

5 Answers5

5

Please do not confuse how something should look when its printed vs. what it should represent as its value.

Printing is never a problem:

>>> ['{0:.1f}'.format(float(i)/10) for i in range(-23,3)]
['-2.3', '-2.2', '-2.1', '-2.0', '-1.9', '-1.8', '-1.7', '-1.6', '-1.5', '-1.4', '-1.3', '-1.2', '-1.1', '-1.0', '-0.9', '-0.8', '-0.7', '-0.6', '-0.5', '-0.4', '-0.3', '-0.2', '-0.1', '0.0', '0.1', '0.2']

But these are strings, because that's their printed representation. When you are doing calculations, you need the float representation which is not exact; as @mitnk pointed out; and as @6502 explained.

You can also use the decimal module which provides more human-friendly representation of floating point numbers.

Burhan Khalid
  • 152,028
  • 17
  • 215
  • 255
3

The reason is -2.3 basically cannot precisely represented as bits, and -2.2999999999999998 is the closest one to -2.3 and can represented as bits.

More reading: http://docs.python.org/2/tutorial/floatingpoint.html http://en.wikipedia.org/wiki/Floating_point

mitnk
  • 2,569
  • 2
  • 18
  • 27
2

It's just how floating point numbers work.. They are not precise

When displaying use a format

print "%.2f" % a
evhen14
  • 1,749
  • 11
  • 16
1

The only numbers that can be represented exactly in a standard IEEE floating point format are of the form num/den where both are integers and where den is an integral power of two.

This means that numbers like 1/10 must be approximated for example to 900719925474099/9007199254740992 that is close enough to 1/10 but that has 1<<53 as denominator.

In other words 1/10 = 0.1 is a number that when expressed in binary form is periodic and would require an infinite number of digits to be represented. What happens is that 1/10 is truncated to a fixed number of binary digits and can only be represented with approximation.

0.1 is impossible to represent in binary exactly like 1/3 = 0.333333... is impossible to represent with a finite number of digits in decimal.

Python is simply different from other languages as it tries by default to tell the truth when converting a number in decimal for printing. You can of course "round" the number for printing but the fact that 1.3 is for example impossible to represent exactly in IEEE floating point remains.

Note also that range(start, stop) will generate elements from start to stop but excluding stop (half-open interval logic). If you want also that element then use range(start, stop+1).

A floating-point interval sampling function is present in NumPy but is not part of the standard Python library. In the versions I prefer however I don't specify a step but the number of samples (to avoid accuracy problems):

def sample_interval(a, b, n):
    delta = float(b - a) / n
    a += delta / 2
    return [a + i*delta for i in xrange(n)]
6502
  • 104,192
  • 14
  • 145
  • 251
  • There should be some round function that gives me what I want. Nothing to do with floating point arithmetic. – user2763361 Nov 20 '13 at 07:24
  • @user2763361 Upgrade to Python 2.7. Done. (Yes, it has to do with binary floating point arithmetic, but if you don't want to know that right now, then you don't want to know.) – Lennart Regebro Nov 20 '13 at 07:33
  • @LennartRegebro I know about this. But I want a solution that can round it away. – user2763361 Nov 20 '13 at 08:09
  • 2
    @user2763361: there's no way to "round it away". `0.1` is simply a number that cannot be expressed exactly in binary, very like 1/3=0.33333333... is a number that cannot be expressed exactly in decimal (0.1 in other words is periodic in binary and thus must be truncated). You can round what you **print**, but `0.1` as an IEEE floating point number simply does not exist and cannot be used in computations. – 6502 Nov 20 '13 at 08:16
  • Oh, the mythical "Do What I Meant" instruction. – Tadeusz A. Kadłubowski Nov 20 '13 at 14:23
0

The answers so far explain what is happening, but there is one more important piece to the puzzle. You are using Python 2.x, if you were using Python 3 (and unless you have a reason not to, you really should), you would never have asked the question.

In Python 3:

>>> [float(i)/10 for i in range(-23,2)]
[-2.3, -2.2, -2.1, -2.0, -1.9, -1.8, -1.7, -1.6, -1.5, -1.4, -1.3, -1.2, -1.1, -1.0, -0.9, -0.8, -0.7, -0.6, -0.5, -0.4, -0.3, -0.2, -0.1, 0.0, 0.1]

The numbers are still stored in exactly the same way, but Python 3 knows that both -2.2999999999999998 and -2.3 have the same representation, so it chooses the format with fewer digits by default. You still need to understand that it hasn't stored a value that is exactly -2.3, but at least your default output looks a bit cleaner.

Duncan
  • 79,697
  • 10
  • 108
  • 148