lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


Minus zero is one of those arcane things which one tends to just write off as an arcane corner of IEEE-754 arithmetic, but it is actually sometimes useful. For example, it avoids a discontinuity in the definition of atan2 if one of the arguments underflows.

Lua 5.1 actually allows you to enter -0 as a constant, which is handy:

Lua 5.1  Copyright (C) 1994-2006 Lua.org, PUC-Rio
> = -0
-0
> = 0
0

So we can show the behaviour of atan2:

> for _, x in ipairs{minus0, zero} do for _, y in ipairs{minus0, zero} do
>>  print("atan2("..x..","..y..")="..math.atan2(x, y))
>> end end
atan2(-0,-0)=-3.1415926535898
atan2(-0,0)=-0
atan2(0,-0)=3.1415926535898
atan2(0,0)=0

Now, how to explain this?

> = math.atan2(-0, -0)
-3.1415926535898
> = math.atan2(-0, 0)
-3.1415926535898
> = math.atan2(0, -0)
0
> = math.atan2(0, 0)
0

The answer is that when lua is compiling a chunk, it keeps a table of constants (strings and numbers) used in the chunk. The table's key is the constant; this allows the compiler to avoid storing the "same" constant twice.

However, Lua tables carefully respect numerical equality (as opposed to identity) with the result that NaN is not a valid key, and that -0 and 0 are considered the same key. So whichever of -0 and 0 is the first "zero" encountered in the chunk is the one which goes into the table. Consequently:

> print(0, -0)
0       0
> print(-0, 0)
-0      -0

Furthermore, the Lua 5.1 compiler precomputes constant numeric expressions. Hence:

> -- This compiles into LOADK / RETURN
> = -4e-324/2
-0

And consequently:

> = 0, -4e-324/2
0       0
> = -4e-324/2, 0
-0      -0

All of this is part of a question I encountered while trying to implement IEEE 754r decimal floating point arithmetic. 754r decimal floating point numbers are not normalized, unlike binary floating point numbers. Consequently 80 and 80.00 have different representations, although they are numerical equal. This makes sense because the main use of decimal floating point would be financial calculations, where the quantities tend to have the same number of fractional digits; keeping the numbers unnormalized is much faster in common cases (as well as generally providing a natural display representation.) More rationale can be found here: http://www2.hursley.ibm.com/decimal/decifaq4.html#unnari

However, it appears that the semantics of Lua tables with respect to numbers is that numeric keys are hashed by equality, not identity. So a decimal floating point number would need to be normalized to insert it into a table. On the other hand, this would create possibly surprising results, similar to the above but magnified. In a hypothetical implementation, one might observe:

-- stand-alone interpreter, each line is a separate chunk
> quantity = 2
> price = 2.00
> = quantity * price
4.00

-- All in one chunk
> do
>>  quantity = 2
>>  price = 2.00
>>  return quantity * price
> end
4
> do
>>  price = 2.00
>>  quantity = 2
>>  return quantity * price
> end
4.0000

I would think that both the latter results would be surprising.

Of course, Lua does not implement decimal arithmetic "out of the box", so the question is at least partially hypothetical. Still, I'm curious what people think would be the correct implementation approach.