tags:

views:

201

answers:

4

Say I have two Python floats a and b, is there an easy way to find out how many representable real numbers are between the two in IEEE-754 representation (or whatever representation the machine used is using)?

+3  A: 

For positive numbers b > a > 0, the answer is approximately:

(2**52) ** (log(b,2) - log(a,2))

There are 52 bits of mantissa ( past the implied 1 ), multiplied by 2 raised to an exponent.

So there are 2**52 numbers in range [1:2) as in the range [1024:2048)

Mark Borgerding
The teory is good - but it does not work for me when the float numbers are too close. Possibly due to the fact that the log calculation introduces some rounding itself.
jsbueno
+8  A: 

I don'tknow what you will be using this for - but, if both floats have the same exponent, it should be possible. As the exponent is kept on the high order bits, loading the float bytes (8 bytes in this case) as an integer and subtracting one from another should give the number you want. I use the struct model to pack the floats to a binary representation, and then unpack those as (C, 8 byte) long ints:

>>> import struct
>>> a = struct.pack("dd", 1.000000,1.000001)
>>> b = struct.unpack("ll",a)
>>> b[1] - b[0]
4503599627
>>> a = struct.pack("dd", 1.000000000,1.000000001)
>>> b = struct.unpack("ll",a)
>>> b[1] - b[0]
4503600
>>>
jsbueno
turns out the exponent doesn't matter, but the sign does.
Chris Dodd
+1; nice answer. It's not going to work on systems with a 32-bit C long, though. To be on the safe side, use "<dd" instead of "dd" and "<qq" instead of "ll"; the "<" forces the struct module to use standard sizes rather than native ones. (With native sizes, "l" is 4 bytes on 32-bit operating systems and on 32-bit *or* 64-bit Windows, and 8 bytes on a typical 64-bit non-Windows OS.)
Mark Dickinson
To be complete, you might also mention that this solution only works for IEEE 754 formats. In practice, that's not much of a restriction, though---it's very difficult to find Python running on a platform using non IEEE 754 doubles.
Mark Dickinson
+9  A: 

AFAIK, IEEE754 floats have an interesting property. If you have float f, then

(*(int*)&f + 1)

under certain conditions, is the next representable floating point number. So for floats a and b

*(int*)&a - *(int*)&b

Will give you the amount of floating point numbers between those numbers.

See http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm for more information.

Matias Valdenegro
The only requirement is that they have the same sign, and neither is Nan or inf.
Chris Dodd
Looks like it's *the* answer to the question, but one part is missing: how does one do that in Python?
Bolo
While this does not answer the question for Python, this is a very good answer. My textbook on "Computational Methods" agrees with this as well.Anyone know of a way to convert this to Python?
Dragontamer5788
@Dragontamer5788 In the meantime I noticed @jsbueno's answer which utilizes struct.pack/unpack in order to do that conversion.
Bolo
I believe http://stackoverflow.com/questions/5415/convert-bytes-to-floating-point-numbers-in-python will help convert the code to python.
Matias Valdenegro
This conversion is what I am doing with the python struct module on my answer. I pack doubles and unpack long ints.
jsbueno
@Chris: Why is Inf an exception?
dan04
A: 

I would look at the frexp function in the math module. The example below extracts the mantissa and converts it to an integer. The difference should be the number of floats between to the two values.

>>> math.frexp(1.1234567890)[0] * 2**53
5059599576307254.0
>>> math.frexp(1.12345678901)[0] * 2**53
5059599576352290.0

The following code should do it:

import math
import sys

def delta(x,y):
    '''Return the number of floats between x and y.'''
    x = float(x)
    y = float(y)
    if x == y:
        return 0
    elif x < y:
        return -delta(y,x)
    else:
        x_mant, x_exp = math.frexp(x)
        y_mant, y_exp = math.frexp(y)
        x_int = int(x_mant * 2**(sys.float_info.mant_dig + x_exp - y_exp))
        y_int = int(y_mant * 2**sys.float_info.mant_dig)
        return x_int - y_int

print(delta(1.123456789, 1.1234567889999))
450
>>>
casevh