84

How can I increment a floating point value in python by the smallest possible amount?


Background: I'm using floating point values as dictionary keys.

Occasionally, very occasionally (and perhaps never, but not certainly never), there will be collisions. I would like to resolve these by incrementing the floating point value by as small an amount as possible. How can I do this?

In C, I would twiddle the bits of the mantissa to achieve this, but I assume that isn't possible in Python.

James
  • 24,676
  • 13
  • 84
  • 130
  • 3
    Due to the sheer amount of activity on this question, it seems to be the canonical "duplicate of" question when linking/closing other "next floating point value in Python" questions. However, it suffers from having at least two disjoint aspects: (1) how to increment a floating point value, and (2) how to prevent collisions when using floats as dict keys. A **much clearer** statement of the title question, and a **much more definitive answer** can be found [here](http://stackoverflow.com/questions/10420848/how-do-you-get-the-next-value-in-the-floating-point-sequence). – John Y Dec 03 '13 at 16:27

15 Answers15

116

Since Python 3.9 there is math.nextafter in the stdlib. Read on for alternatives in older Python versions.

Increment a python floating point value by the smallest possible amount

The nextafter(x,y) functions return the next discretely different representable floating-point value following x in the direction of y. The nextafter() functions are guaranteed to work on the platform or to return a sensible value to indicate that the next value is not possible.

The nextafter() functions are part of POSIX and ISO C99 standards and is _nextafter() in Visual C. C99 compliant standard math libraries, Visual C, C++, Boost and Java all implement the IEEE recommended nextafter() functions or methods. (I do not honestly know if .NET has nextafter(). Microsoft does not care much about C99 or POSIX.)

None of the bit twiddling functions here fully or correctly deal with the edge cases, such as values going though 0.0, negative 0.0, subnormals, infinities, negative values, over or underflows, etc. Here is a reference implementation of nextafter() in C to give an idea of how to do the correct bit twiddling if that is your direction.

There are two solid work arounds to get nextafter() or other excluded POSIX math functions in Python < 3.9:

Use Numpy:

>>> import numpy
>>> numpy.nextafter(0,1)
4.9406564584124654e-324
>>> numpy.nextafter(.1, 1)
0.10000000000000002
>>> numpy.nextafter(1e6, -1)
999999.99999999988
>>> numpy.nextafter(-.1, 1)
-0.099999999999999992

Link directly to the system math DLL:

import ctypes
import sys
from sys import platform as _platform

if _platform == "linux" or _platform == "linux2":
    _libm = ctypes.cdll.LoadLibrary('libm.so.6')
    _funcname = 'nextafter'
elif _platform == "darwin":
    _libm = ctypes.cdll.LoadLibrary('libSystem.dylib')
    _funcname = 'nextafter'
elif _platform == "win32":
    _libm = ctypes.cdll.LoadLibrary('msvcrt.dll')
    _funcname = '_nextafter'
else:
    # these are the ones I have access to...
    # fill in library and function name for your system math dll
    print("Platform", repr(_platform), "is not supported")
    sys.exit(0)

_nextafter = getattr(_libm, _funcname)
_nextafter.restype = ctypes.c_double
_nextafter.argtypes = [ctypes.c_double, ctypes.c_double]

def nextafter(x, y):
    "Returns the next floating-point number after x in the direction of y."
    return _nextafter(x, y)

assert nextafter(0, 1) - nextafter(0, 1) == 0
assert 0.0 + nextafter(0, 1) > 0.0

And if you really really want a pure Python solution:

# handles edge cases correctly on MY computer 
# not extensively QA'd...
import math
# 'double' means IEEE 754 double precision -- c 'double'
epsilon  = math.ldexp(1.0, -53) # smallest double that 0.5+epsilon != 0.5
maxDouble = float(2**1024 - 2**971)  # From the IEEE 754 standard
minDouble  = math.ldexp(1.0, -1022) # min positive normalized double
smallEpsilon  = math.ldexp(1.0, -1074) # smallest increment for doubles < minFloat
infinity = math.ldexp(1.0, 1023) * 2

def nextafter(x,y):    
    """returns the next IEEE double after x in the direction of y if possible"""
    if y==x:
       return y         #if x==y, no increment
             
    # handle NaN
    if x!=x or y!=y:
        return x + y       
    
    if x >= infinity:
        return infinity
        
    if x <= -infinity:
        return -infinity

    if -minDouble < x < minDouble:
        if y > x:
            return x + smallEpsilon
        else:
            return x - smallEpsilon  
        
    m, e = math.frexp(x)        
    if y > x:
        m += epsilon
    else:
        m -= epsilon
        
    return math.ldexp(m,e)

Or, use Mark Dickinson's excellent solution

Obviously the Numpy solution is the easiest.

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
  • 7
    +1. Thanks for being the first person to actually answer the question. – A. Rex May 29 '11 at 04:09
  • 3
    Python has `Decimal.next_plus()` http://stackoverflow.com/questions/5749188/does-python-have-an-equivalent-to-java-lang-math-nextup/5756149#5756149 – jfs Jul 05 '11 at 17:31
  • 1
    One edge case not discussed: how to come up with some number bigger than `x` to give to the `nextafter` function. Suppose you just always provide `x + 1` as the `y` argument; that will give you the wrong answer if `x` is very near the maximum possible value. Perhaps it would be more convenient to just consider the sign of the `y` argument to `nextafter`, to indicate whether increment or decrement is desired. – wberry Mar 12 '12 at 21:28
  • 1
    @wberry What about using +inf or -inf for y ? –  Jan 17 '13 at 21:06
  • I'm a bit red-faced that I didn't think of that... seems to work fine. – wberry Jan 20 '13 at 18:48
  • It not works see: http://stackoverflow.com/questions/20246965/how-to-add-to-substract-from-python-float-value-the-smallest-possible-value-exp – Chameleon Nov 27 '13 at 15:58
  • Note that the pure Python solution doesn't handle all cases correctly, e.g 'nextafter(0,1)' and 'nextafter(1,0)' return wrong results.. If you want a pure Python solution, look into this answer to a duplicate instead, which appears to be more robust: http://stackoverflow.com/a/10426033/49793 – oefe Dec 03 '13 at 20:55
  • also, for infinity, I'd use `float('inf')` instead of `math.ldexp(1.0, 1023) * 2` – Brian Minton Dec 18 '13 at 20:38
  • 1
    You might find this useful. `from _testcapi import DBL_MAX, DBL_MIN, FLT_MAX, FLT_MIN` – JohnMudd Nov 11 '14 at 18:58
  • Decimal.next_plus doesn't seem to do what you want as float(d) == float(d.next_plus()) is true – boxed Nov 10 '16 at 22:04
  • Note that numpy.nextafter returns a numpy float64, not a python float. You may want to cast it back to a python float afterwards. – plugwash Apr 05 '19 at 13:35
16

Python 3.9 and above

Starting with Python 3.9, released 2020-10-05, you can use the math.nextafter function:

math.nextafter(x, y)

Return the next floating-point value after x towards y.

If x is equal to y, return y.

Examples:

  • math.nextafter(x, math.inf) goes up: towards positive infinity.

  • math.nextafter(x, -math.inf) goes down: towards minus infinity.

  • math.nextafter(x, 0.0) goes towards zero.

  • math.nextafter(x, math.copysign(math.inf, x)) goes away from zero.

See also math.ulp().

A simpler alternative to math.copysign(math.inf, x) is to simply substitute 2*x.

gerrit
  • 24,025
  • 17
  • 97
  • 170
9

First, this "respond to a collision" is a pretty bad idea.

If they collide, the values in the dictionary should have been lists of items with a common key, not individual items.

Your "hash probing" algorithm will have to loop through more than one "tiny increments" to resolve collisions.

And sequential hash probes are known to be inefficient.

Read this: http://en.wikipedia.org/wiki/Quadratic_probing

Second, use math.frexp and sys.float_info.epsilon to fiddle with mantissa and exponent separately.

>>> m, e = math.frexp(4.0)
>>> (m+sys.float_info.epsilon)*2**e
4.0000000000000018
S.Lott
  • 384,516
  • 81
  • 508
  • 779
  • +1 for using lists. `defaultdict(list)` can be used so you can just do `mydict[key].append(value)` without worrying if the key already exists. – kindall May 19 '11 at 19:58
  • I am aware of all sorts of fancy many-stage hashing techniques, but I'd like to do something quick and simple, which I **know** will be adequate in this case. – James May 19 '11 at 20:05
  • And additionally, I know that there are no keys with values greater than the current time, so incrementing is a sensible way to resolve collisions. – James May 19 '11 at 20:14
  • 2
    @Autopulated, there may be keys greater than the current time - if there's already been a collision! – Mark Ransom May 19 '11 at 20:23
  • Yes, so my timer would need to have returned the same value *three* times. I don't know how fast your computer is, but the ones I care about are nowhere near that fast. – James May 19 '11 at 20:36
  • If you don't want to change the processing code to use lists, you could change the key to be a tuple of `(time, 0)` and then use `(time, 1)` when there's a duplicate. Obviously you could also continue incrementing the second value in the event of more duplicates. – kindall May 19 '11 at 23:05
  • 1
    @Autopulated you haven't told us the nature of the events you're inserting so I have no idea how likely it is to get more than two events per tick. Also note that some timers don't count as often as their precision would suggest. Three in a row isn't the only way to trip this problem, you could also have two followed by two more in the next tick. – Mark Ransom May 20 '11 at 00:59
  • 4.0000000000000018 isn't the smallest representable float value greater than 4. – James May 23 '11 at 13:53
  • @Autopulated: What is the smallest? Please provide the value using Python, so that Python's floating-point formatting can display it consistently with other values. – S.Lott May 23 '11 at 14:29
  • Also, I think different platforms are having an effect here: your code on my machine gives 4.000000000000002 – James May 23 '11 at 14:37
  • @Autopulated: How did you arrive at that `4.000000000000001` value? When I enter that at the comment prompt, Python responds `4.0000000000000009`. Causing me to doubt that the value can actually be represented. – S.Lott May 23 '11 at 15:34
  • I tried some values smaller than 4.000000000000002, and that was the resulting representation (what python prints back to me) -- it appears that your internal floating point representation handles rounding differently to mine, but the principle is the same. – James May 23 '11 at 15:37
  • @Autopulated: "some values smaller". Fine. And when you did subtraction, what value did you get? Why can't you use that value instead of the `sys.float_info.epsilon`? What stops you from using the value you discovered? – S.Lott May 23 '11 at 15:39
  • Well, presuming that because the mantissa is in the range [0,0.5) and the sys.float_info.epsilon is for the value 1.0, I could get away with adding half of the epsilon to the mantissa, I tried that, and it seems to work. I'm not sure about edge cases though. – James May 23 '11 at 15:46
  • @Autopulated: That **is** the edge case. – S.Lott May 23 '11 at 15:49
  • Isn't the mantissa in [0,0.5) for all (I think?) floating point values? – James May 23 '11 at 15:55
  • @Autopulated: That's a simple thing to look up in the IEEE floating-point specification. Or here. http://en.wikipedia.org/wiki/IEEE_754-2008 – S.Lott May 23 '11 at 16:38
  • Sorry, [0.5,1) for all values other than 0. But that still means dividing epsilon by 2 ([0,0.5) would have implied dividing by 4). – James May 23 '11 at 17:35
  • @James: yes. `((2*m+sys.float_info.epsilon)*2**(e-1))` gives value closer to `nextafter(4, float('+inf'))` value. – jfs Jan 14 '17 at 14:33
  • Sorry, I've upvoted a lot of your answers but the code here is incorrect even for the example given. The next float after 4.0 (=4503599627370496/2^50) is 4503599627370497/2^50 (exact value 4.00000000000000088817841970012523233890533447265625, printed [since Python 2.7](https://mail.python.org/pipermail/python-dev/2009-October/092958.html) as 4.000000000000001). What `m, e = math.frexp(4.0); (m+sys.float_info.epsilon)*2**e` (the code at the bottom of this answer) gives is 4503599627370498/2^50 (exact value 4.0000000000000017763568394002504646778106689453125, printed as 4.000000000000002). – ShreevatsaR Jun 02 '18 at 00:41
5

Forgetting about why we would want to increment a floating point value for a moment, I would have to say I think Autopulated's own answer is probably correct.

But for the problem domain, I share the misgivings of most of the responders to the idea of using floats as dictionary keys. If the objection to using Decimal (as proposed in the main comments) is that it is a "heavyweight" solution, I suggest a do-it-yourself compromise: Figure out what the practical resolution is on the timestamps, pick a number of digits to adequately cover it, then multiply all the timestamps by the necessary amount so that you can use integers as the keys. If you can afford an extra digit or two beyond the timer precision, then you can be even more confident that there will be no or fewer collisions, and that if there are collisions, you can just add 1 (instead of some rigamarole to find the next floating point value).

John Y
  • 14,123
  • 2
  • 48
  • 72
4

Instead of incrementing the value, just use a tuple for the colliding key. If you need to keep them in order, every key should be a tuple, not just the duplicates.

Mark Amery
  • 143,130
  • 81
  • 406
  • 459
Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
  • 4
    *Any* `float` is less than *any* `tuple`. `4.0 < ()` --> `True` – kindall May 19 '11 at 19:54
  • 1
    @kindall, thanks! One of the things I love about this site is when your own answer teaches you something new. – Mark Ransom May 19 '11 at 20:18
  • @Adam, it appears some people found my mistake and rescinded their votes. I had `` going through the bad example but it only showed on IE, so my edit left the answer in a very confusing state. Should be fixed now. – Mark Ransom May 20 '11 at 00:47
  • 3
    @kindall, incompatible types are orderable in Python 2, but not Python 3. You'll get a `TypeError` if you use `<` between a float and a tuple. – bignose May 25 '11 at 06:29
  • @kindall, in Python 3, floats and tuples aren't directly comparable.`TypeError: unorderable types: float() < tuple()` – Brian Minton Dec 18 '13 at 20:40
  • Yes, that's true, as pointed out in the comment right above yours that was posted 2.5 years ago. – kindall Dec 18 '13 at 20:44
4

I recommend against assuming that floats (or timestamps) will be unique if at all possible. Use a counting iterator, database sequence or other service to issue unique identifiers.

wberry
  • 18,519
  • 8
  • 53
  • 85
  • I'm not assuming that they're going to be unique, hence the question! I am assuming collisions are very rare though. – James May 19 '11 at 19:42
  • 4
    @Autopulated: "am assuming collisions are very rare" just as bad as assuming they don't happen. Find a better key. – S.Lott May 19 '11 at 19:51
  • No, collisions are very rare. I know how my timer behaves, and I know when things are added to my dictionary: rare hash collisions are perfectly acceptable. – James May 19 '11 at 20:04
3

A better answer (now I'm just doing this for fun...), motivated by twiddling the bits. Handling the carry and overflows between parts of the number of negative values is somewhat tricky.

import struct

def floatToieee754Bits(f):
    return struct.unpack('<Q', struct.pack('<d', f))[0]

def ieee754BitsToFloat(i):
    return struct.unpack('<d', struct.pack('<Q', i))[0]

def incrementFloat(f):
    i = floatToieee754Bits(f)
    if f >= 0:
        return ieee754BitsToFloat(i+1)
    else:
        raise Exception('f not >= 0: unsolved problem!')
James
  • 24,676
  • 13
  • 84
  • 130
  • I like this one, though I didn't understand it to begin with. (I Read: http://docs.python.org/library/struct.html and now understand it better.) The only thing i'm not a fan on is the function naming. ;) – James Khoury May 26 '11 at 23:16
  • 1
    This is what I came here to post. It is the simplest, most robust, and provably correct answer. Of course if you have a collision at Inf, you'll wrap around... – Gabe May 28 '11 at 17:31
  • This is also essentially Mark Dickinson's answer [here](http://stackoverflow.com/questions/10420848/how-do-you-get-the-next-value-in-the-floating-point-sequence). – John Y Dec 03 '13 at 16:17
  • 1
    [here's a version of this solution that support negative floats too](http://stackoverflow.com/a/10426033/4279) – jfs Jan 14 '17 at 14:35
2

Instead of resolving the collisions by changing the key, how about collecting the collisions? IE:

bag = {}
bag[1234.] = 'something'

becomes

bag = collections.defaultdict(list)
bag[1234.].append('something')

would that work?

SingleNegationElimination
  • 151,563
  • 33
  • 264
  • 304
2

For colliding key k, add: k / 250


Interesting problem. The amount you need to add obviously depends on the magnitude of the colliding value, so that a normalized add will affect only the least significant bits.

It's not necessary to determine the smallest value that can be added. All you need to do is approximate it. The FPU format provides 52 mantissa bits plus a hidden bit for 53 bits of precision. No physical constant is known to anywhere near this level of precision. No sensor is able measure anything near it. So you don't have a hard problem.

In most cases, for key k, you would be able to add k/253, because of that 52-bit fraction plus the hidden bit.

But it's not necessary to risk triggering library bugs or exploring rounding issues by shooting for the very last bit or anything near it.

So I would say, for colliding key k, just add k / 250 and call it a day.1


1. Possibly more than once until it doesn't collide any more, at least to foil any diabolical unit test authors.

DigitalRoss
  • 143,651
  • 25
  • 248
  • 329
  • Oh, and for zero, you could do something different, because a vastly smaller quantity based on the *range* (rather than the *precision)* of double values can be used. Add something like `1 / 2 ** 1020.` – DigitalRoss May 26 '11 at 16:21
1
import sys
>>> sys.float_info.epsilon
2.220446049250313e-16
Adam Byrtek
  • 12,011
  • 2
  • 32
  • 32
  • 4
    If you add this number to, say, 4.0, you will get a value that is exactly the same! – James May 19 '11 at 19:40
  • 3
    I think the proper usage would be `x+=x*sys.float_info.epsilon` – Mark Ransom May 19 '11 at 19:45
  • 7
    `sys.float_info.epsilon` is defined at the "smallest difference between 1.0 and the next largest value representable", so isn't safe to use relative to other values. – martineau May 19 '11 at 20:27
  • 3
    That's right, the smallest difference depends on the exponent. – Adam Byrtek May 19 '11 at 20:27
  • 3
    @Mark is correct, it looks robust with big/small floats. Also, for version < 2.6 where there isn't any float_info, you can define `epsilon = 2*pow(2, -53)` – Mike T May 24 '11 at 03:02
  • Or the usage `x = x * (1+sys.float_info.epsilon)`. But you need to state the usage in your answer. Otherwise people might naively try to add: `x + sys.float_info.epsilon`, which would fail due to underflow on floats with absval > 1, and be too large on floats < 1. Btw, x could be negative (@MarkRansom). – smci Apr 10 '20 at 05:13
  • @smci my answer is completely different. The comment I left here should work for any `x`, including negative numbers. And you're right, this answer needs an example of usage to be complete. – Mark Ransom Apr 10 '20 at 12:20
  • @MarkRansom: I know your answer's completely different (and the premise of the question is dodgy). I'm saying this answer needs to explain the correct usage. – smci Apr 10 '20 at 20:23
1

Instead of modifying your float timestamp, use a tuple for every key as Mark Ransom suggests where the tuple (x,y) is composed of x=your_unmodified_time_stamp and y=(extremely unlikely to be a same value twice).

So:

  1. x just is the unmodified timestamp and can be the same value many times;
  2. y you can use:
    1. a random integer number from a large range,
    2. serial integer (0,1,2,etc),
    3. UUID.

While 2.1 (random int from a large range) there works great for ethernet, I would use 2.2 (serializer) or 2.3 (UUID). Easy, fast, bulletproof. For 2.2 and 2.3 you don't even need collision detection (you might want to still have it for 2.1 as ethernet does.)

The advantage of 2.2 is that you can also tell, and sort, data elements that have the same float time stamp.

Then just extract x from the tuple for any sorting type operations and the tuple itself is a collision free key for the hash / dictionary.

Edit

I guess example code will help:

#!/usr/bin/env python

import time
import sys
import random

#generator for ints from 0 to maxinteger on system:
serializer=(sn for sn in xrange(0,sys.maxint))

#a list with guranteed collisions:
times=[]
for c in range(0,35):
   t=time.clock()
   for i in range(0,random.choice(range(0,4))):
      times.append(t)

print len(set(times)), "unique items in a list of",len(times)      

#dictionary of tuples; no possibilities of collisions:
di={}   
for time in times:
    sn=serializer.next()
    di[(time,sn)]='Element {}'.format(sn)

#for tuples of multiple numbers, Python sorts
# as you expect: first by t[0] then t[1], until t[n]
for key in sorted(di.keys()):
    print "{:>15}:{}".format(key, di[key]) 

Output:

26 unique items in a list of 55
  (0.042289, 0):Element 0
  (0.042289, 1):Element 1
  (0.042289, 2):Element 2
  (0.042305, 3):Element 3
  (0.042305, 4):Element 4
  (0.042317, 5):Element 5
  # and so on until Element n...
Community
  • 1
  • 1
dawg
  • 98,345
  • 23
  • 131
  • 206
  • The approach seems hacky and complicated/unworkable. How would you retrieve a dict value in O(1) without already knowing the "y value" corresponding per each key? It seems to be just moving the same problem elsewhere. – wim Apr 09 '20 at 22:02
1

Here it part of it. This is dirty and slow, but maybe that is how you like it. It is missing several corner cases, but maybe this gets someone else close.

The idea is to get the hex string of a floating point number. That gives you a string with the mantissa and exponent bits to twiddle. The twiddling is a pain since you have to do all it manually and keep converting to/from strings. Anyway, you add(subtract) 1 to(from) the last digit for positive(negative) numbers. Make sure you carry through to the exponent if you overflow. Negative numbers are a little more tricky to make you don't waste any bits.

def increment(f):
    h = f.hex()
    # decide if we need to increment up or down
    if f > 0:
        sign = '+'
        inc = 1
    else:
        sign = '-'
        inc = -1
    # pull the string apart
    h = h.split('0x')[-1]
    h,e = h.split('p')
    h = ''.join(h.split('.'))
    h2 = shift(h, inc)
    # increase the exponent if we added a digit
    h2 = '%s0x%s.%sp%s' % (sign, h2[0], h2[1:], e)
    return float.fromhex(h2)

def shift(s, num):
    if not s:
        return ''
    right = s[-1]
    right = int(right, 16) + num
    if right > 15:
        num = right // 16
        right = right%16
    elif right < 0:
        right = 0
        num = -1
    else:
        num = 0
    # drop the leading 0x
    right = hex(right)[2:]
    return shift(s[:-1], num) + right

a = 1.4e4
print increment(a) - a
a = -1.4e4
print increment(a) - a

a = 1.4
print increment(a) - a
matt
  • 4,089
  • 1
  • 20
  • 17
0

I think you mean "by as small an amount possible to avoid a hash collision", since for example the next-highest-float may already be a key! =)

while toInsert.key in myDict: # assumed to be positive
    toInsert.key *= 1.000000000001
myDict[toInsert.key] = toInsert

That said you probably don't want to be using timestamps as keys.

ninjagecko
  • 88,546
  • 24
  • 137
  • 145
  • The **actual** epsilon varies on the value of the exponent though. I'm just going to keep adding until I get to a new value since collisions will be very rare. – James May 19 '11 at 19:39
  • ah oops, I realized that, just wasn't thinking; I'd actually then recommend multiplying; answer edited – ninjagecko May 19 '11 at 23:53
0

After Looking at Autopopulated's answer I came up with a slightly different answer:

import math, sys

def incrementFloatValue(value):
    if value == 0:
        return sys.float_info.min                                
    mant, exponent = math.frexp(value)                                                   
    epsilonAtValue = math.ldexp(1, exponent - sys.float_info.mant_dig)                
    return math.fsum([value, epsilonAtValue])

Disclaimer: I'm really not as great at maths as I think I am ;) Please verify this is correct before using it. Also I'm not sure about performance

some notes:

  • epsilonAtValue calculates how many bits are used for the mantissa (the maximum minus what is used for the exponent).
  • I'm not sure if the math.fsum() is needed but hey it doesn't seem to hurt.
James
  • 24,676
  • 13
  • 84
  • 130
James Khoury
  • 21,330
  • 4
  • 34
  • 65
-1

It turns out that this is actually quite complicated (maybe why seven people have answered without actually providing an answer yet...).

I think this is the right solution, it certainly seems to handle 0 and positive values correctly:

import math
import sys

def incrementFloat(f):
    if f == 0.0:
        return sys.float_info.min
    m, e = math.frexp(f)
    return math.ldexp(m + sys.float_info.epsilon / 2, e)
James
  • 24,676
  • 13
  • 84
  • 130
  • 2
    I provided an answer to the problem, just not an answer to the question! Sorry you didn't like it. – Mark Ransom May 24 '11 at 03:20
  • 4
    @Mark I appreciate that people are trying to be helpful (I did upvote most of the answers), but really the problem *is* the question. My thinking is partly along the lines of "what if someone else searched for how to increment a float by the smallest possible amount in the future" -- if they found a page full of tuples and discussion about hash collisions it wouldn't really much help. – James May 24 '11 at 09:12
  • 1
    If the people providing answers believe that using floats as keys is inherently a bad idea, then they are going to suggest staying away from floats as keys to anyone else who has the same question. – John Y May 25 '11 at 19:58