• Subject: Re: Numeric key collision related bug in Lua 5.3
• From: Dirk Laurie <dirk.laurie@...>
• Date: Tue, 21 Apr 2015 20:54:16 +0200

```2015-04-21 18:36 GMT+02:00 Dirk Laurie <dirk.laurie@gmail.com>:
> 2015-04-21 17:32 GMT+02:00 Egor Skriptunoff <egor.skriptunoff@gmail.com>:
>> An interesting bug has been found in Lua 5.3
>>
>> t = {[(1<<63)-333] = 0}
>> key = next(t) + 0.0
>> t[key] = "Lua is great!"
>> print(t[key])          --> Lua is great!
>> t[0] = "Are you sure?"
>> print(t[key])          --> nil
>>
>> Why Lua is not great anymore?
>
> The manual says:
>
> The indexing of tables follows the definition of raw equality in the
> language. The expressions a[i] and a[j] denote the same table element
> if and only if i and j are raw equal (that is, equal without
> metamethods). In particular, floats with integral values are equal to
> their respective integers (e.g., 1.0 == 1). To avoid ambiguities, any
> float with integral value used as a key is converted to its respective
> integer. For instance, if you write a[2.0] = true, the actual key
> inserted into the table will be the integer 2. (On the other hand, 2
> and "2" are different Lua values and therefore denote different table
> entries.)
>
> Let origkey = 1<<63)-333, which  is a very large integer, but slightly
> smaller than math.maxinteger.
>
> "key" is a float with integral value, but that integral value is not
> origkey, but math.maxinteger. In a floating-point comparison, it tests
> equal to origkey,
> though.
>
> When t[key] is assigned, "key" is not considered to be a new index, and
> the value of t[origkey] is replaced.
>
> When t[0] is assigned, the hash part of the table is reorganized.
> t[origkey] is still "Lua is great!", but is no longer found when asking
> for t[key].

I've been asking myself what in all this is a bug. And the answer has
nothing to do with tables.

origkey = (1<<63)-333
maxint = math.maxinteger
key = origkey + 0.
print (maxint == key ) --> true

I.e. a float that can be represented exactly as an integer is being
compared to an integer is being compared to another integer,
and tests equal even though those integers are different.

Somewhat surprisingly, this is in fact the documented behaviour.

| If both operands are integers, they are compared as integers;
| otherwise, they are converted to floats and compared as such.

The ideal solution would be to change the semantics to:

| If both operands are integers, or if one operand is a float but
| can be represented exactly as an integer, they are compared
| as integers; otherwise, they are converted to floats and
| compared as such.

But until such time as that is done, I submit that the "numeric
key collision related bug" is not a bug, but only a gotcha.

```