• Subject: Re: Problem with -0
• From: Jonathan Goble <jcgoble3@...>
• Date: Tue, 24 May 2022 23:46:14 -0400

On Tue, May 24, 2022 at 10:50 PM Eduardo Ochs <eduardoochs@gmail.com> wrote:
Hi list,

a few hours ago I noticed for the first time that print(0*-2) prints
"-0" instead of "0" - because Lua supports
<https://en.wikipedia.org/wiki/Signed_zero> - and I tried to write a
function that would "fix" the "-0"s in the cases that I don't want
them... and well, it turns out that this doesn't work on Lua 5.1.5:

fix0 = function (x) if (x == -0) then return  0 else return x end end

Here's a test:

--snip--snip--

cd

cat > /tmp/testminus0.lua <<'%%%'
f = function (x) if (x ==  0) then return  0 else return x end end
g = function (x) if (x == -0) then return  0 else return x end end --
buggy on Lua5.1
h = function (x) if (x == -0) then return 42 else return x end end
print(f(0), f(-0), f(99))
print(g(0), g(-0), g(99))
print(h(0), h(-0), h(99))
%%%

Here's a couple of disassemblies (main chunk removed to focus on the function):

\$ luac5.1 -l -l -
function fix0(x) if (x == -0) then return  0 else return x end end

function <stdin:1,1> (7 instructions, 28 bytes at 0x55c0112fbba0)
1 param, 2 slots, 0 upvalues, 1 local, 1 constant, 0 functions
1       [1]     EQ              0 0 -1  ; - -0
2       [1]     JMP             3       ; to 6
3       [1]     LOADK           1 -1    ; -0
4       [1]     RETURN          1 2
5       [1]     JMP             1       ; to 7
6       [1]     RETURN          0 2
7       [1]     RETURN          0 1
constants (1) for 0x55c0112fbba0:
1       -0
locals (1) for 0x55c0112fbba0:
0       x       1       7
upvalues (0) for 0x55c0112fbba0:

\$ luac5.1 -l -l -
function fix42(x) if (x == -0) then return 42 else return x end end

function <stdin:1,1> (7 instructions, 28 bytes at 0x5584b577aba0)
1 param, 2 slots, 0 upvalues, 1 local, 2 constants, 0 functions
1       [1]     EQ              0 0 -1  ; - -0
2       [1]     JMP             3       ; to 6
3       [1]     LOADK           1 -2    ; 42
4       [1]     RETURN          1 2
5       [1]     JMP             1       ; to 7
6       [1]     RETURN          0 2
7       [1]     RETURN          0 1
constants (2) for 0x5584b577aba0:
1       -0
2       42
locals (1) for 0x5584b577aba0:
0       x       1       7
upvalues (0) for 0x5584b577aba0:

This gives us a clue: there is no 0 (positive zero) constant compiled into the chunk at all. A bit of poking at the 5.1 source code reveals the addk() function [1], which is responsible for adding a constant. It ultimately performs the test for whether the constant already exists a few levels down using the luai_numeq() macro [2], which simply uses == to compare them... but 0 and -0 are defined by the IEEE standard to be equal, so whichever one (positive or negative zero) is compiled first in a chunk or function will be used for both throughout that chunk or function.