Equivalent to Gamemaker's sign() function?

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
coffeecat
Prole
Posts: 29
Joined: Sun Sep 13, 2015 4:10 pm

Re: Equivalent to Gamemaker's sign() function?

Post by coffeecat »

If you don't, then this sign function becomes positively biased one - it returns 1 if the input is 0.
No, it returns nan if the input is 0. Did you read later comments?

0 can be a common input in some use cases.
User avatar
raidho36
Party member
Posts: 2063
Joined: Mon Jun 17, 2013 12:00 pm

Re: Equivalent to Gamemaker's sign() function?

Post by raidho36 »

I'm saying it because I tested it and it turned out that way. But in fact there's a manual entry that expressly says so. So all these people are ill informed. In that scenario min and max functions return whichever value wasn't NaN.
http://www.cplusplus.com/reference/cmath/fmax/ wrote:If one of the arguments in a NaN, the other is returned.
User avatar
zorg
Party member
Posts: 3441
Joined: Thu Dec 13, 2012 2:55 pm
Location: Absurdistan, Hungary
Contact:

Re: Equivalent to Gamemaker's sign() function?

Post by zorg »

raidho36 wrote: Mon Feb 12, 2018 6:20 am I'm saying it because I tested it and it turned out that way. But in fact there's a manual entry that expressly says so. So all these people are ill informed. In that scenario min and max functions return whichever value wasn't NaN.
http://www.cplusplus.com/reference/cmath/fmax/ wrote:If one of the arguments in a NaN, the other is returned.
The reason i said that NaN was propagated was because i was lazy and tried out the code on the online lua testbed, which is PuC lua 5.3
That said, 5.3 does have integers in it, and as was previously said, math.huge may not be infinity, so my tested result might have been wrong;

Code: Select all

return (function(n) return math.max(math.min(n * (1.0/0.0), 1.0), -1.0) end)(0)
The above code, still returns nan (-nan, but whatever, still not a number) on PuC lua 5.3; interestingly, in löve/luaJIT, it does return 1.
To be honest, i'd rather have even a tight loop take a bit more time if i can avoid an inconsistent result.
Me and my stuff :3True Neutral Aspirant. Why, yes, i do indeed enjoy sarcastically correcting others when they make the most blatant of spelling mistakes. No bullying or trolling the innocent tho.
coffeecat
Prole
Posts: 29
Joined: Sun Sep 13, 2015 4:10 pm

Re: Equivalent to Gamemaker's sign() function?

Post by coffeecat »

I tried in PUC Lua 5.2 before posting the previous comment, and got nan if the input is 0.
raidho36 wrote: Mon Feb 12, 2018 6:20 am But in fact there's a manual entry that expressly says so.
http://www.cplusplus.com/reference/cmath/fmax/ wrote:If one of the arguments in a NaN, the other is returned.
I don't think the C/C++ documentation applies to Lua. The behavior of math.min/math.max with respect to nan is unspecified in the official Lua manual, and therefore LuaJIT can have a different behavior while still be conforming. We shouldn't rely on unspecified behaviors.
User avatar
raidho36
Party member
Posts: 2063
Joined: Mon Jun 17, 2013 12:00 pm

Re: Equivalent to Gamemaker's sign() function?

Post by raidho36 »

LuaJIT as well as Lua use C as a backbone so it applies pretty much directly. I didn't know Lua 5.3 would propagate NaNs against this defined behavior of C language, for whatever reason, but LuaJIT doesn't do that, as it should. Oddly enough, I think there's a bug in LuaJIT: when min and max functions have to deal with NaNs, return value is inconsistent: sometimes it's the other value and sometimes it's NaN. I know it's JIT bug and not interpreter/glibc/hardware bug because it runs fine in interpreted mode, as well as in compiled C.
User avatar
pgimeno
Party member
Posts: 3548
Joined: Sun Oct 18, 2015 2:58 pm

Re: Equivalent to Gamemaker's sign() function?

Post by pgimeno »

raidho36 wrote: Mon Feb 12, 2018 9:00 amLuaJIT as well as Lua use C as a backbone so it applies pretty much directly.
Huh?

Code: Select all

$ cat > test.lua
local function clamp_inf(x)
  return math.max(-1, math.min(1, x*math.huge))
end
local s = 0                         
for i = -1000, 1000 do s = s + clamp_inf(i) end
print(s)

$ luajit -jdump test.lua 
---- TRACE 1 start test.lua:5
0007  MOV      6   0
0008  MOV      7   5
0009  CALL     6   2   2
0000  . FUNCF    6          ; test.lua:1
0001  . GGET     1   0      ; "math"
0002  . TGETS    1   1   1  ; "max"
0003  . KSHORT   2  -1
0004  . GGET     3   0      ; "math"
0005  . TGETS    3   3   2  ; "min"
0006  . KSHORT   4   1
0007  . GGET     5   0      ; "math"
0008  . TGETS    5   5   3  ; "huge"
0009  . MULVV    5   0   5
0010  . CALL     3   0   3
0000  . . FUNCC               ; math.min
0011  . CALLMT   1   1
0000  . FUNCC               ; math.max
0010  ADDVV    1   1   6
0011  FORL     2 => 0007
---- TRACE 1 IR
0001    int SLOAD  #3    CI
0002 >  fun SLOAD  #1    T
0003 >  fun EQ     0002  test.lua:1
0004    tab FLOAD  test.lua:1  func.env
0005    int FLOAD  0004  tab.hmask
0006 >  int EQ     0005  +63 
0007    p32 FLOAD  0004  tab.node
0008 >  p32 HREFK  0007  "math" @54
0009 >  tab HLOAD  0008
0010    int FLOAD  0009  tab.hmask
0011 >  int EQ     0010  +31 
0012    p32 FLOAD  0009  tab.node
0013 >  p32 HREFK  0012  "max" @11
0014 >  fun HLOAD  0013
0015 >  p32 HREFK  0012  "min" @22
0016 >  fun HLOAD  0015
0017 >  p32 HREFK  0012  "huge" @3
0018 >  num HLOAD  0017
0019    num CONV   0001  num.int
0020    num MUL    0019  0018
0021 >  fun EQ     0016  math.min
0022    num MIN    0020  +1  
0023 >  fun EQ     0014  math.max
0024    num MAX    0022  -1  
0025 >  num SLOAD  #2    T
0026  + num ADD    0025  0024
0027  + int ADD    0001  +1  
0028 >  int LE     0027  +1000
0029 ------ LOOP ------------
0030    num CONV   0027  num.int
0031    num MUL    0030  0018
0032    num MIN    0031  +1  
0033    num MAX    0032  -1  
0034  + num ADD    0033  0026
0035  + int ADD    0027  +1  
0036 >  int LE     0035  +1000
0037    int PHI    0027  0035
0038    num PHI    0026  0034
---- TRACE 1 mcode 359
b771fe90  mov dword [0xb77212bc], 0x1
b771fe9a  movsd xmm1, [0xb773f470]
b771fea2  movsd xmm0, [0xb773f478]
b771feaa  cvtsd2si edi, [edx+0x10]
b771feaf  cmp dword [edx+0x4], -0x09
b771feb3  jnz 0xb7718008	->0
b771feb9  cmp dword [edx], 0xb772cbc0
b771febf  jnz 0xb7718008	->0
b771fec5  mov ebp, [0xb772cbc8]
b771fecb  cmp dword [ebp+0x1c], +0x3f
b771fecf  jnz 0xb7718008	->0
b771fed5  mov ebx, [ebp+0x14]
b771fed8  cmp dword [ebx+0x51c], -0x05
b771fedf  jnz 0xb771feeb
b771fee1  cmp dword [ebx+0x518], 0xb7725f30
b771feeb  jnz 0xb7718008	->0
b771fef1  cmp dword [ebx+0x514], -0x0c
b771fef8  jnz 0xb7718008	->0
b771fefe  mov ecx, [ebx+0x510]
b771ff04  cmp dword [ecx+0x1c], +0x1f
b771ff08  jnz 0xb7718008	->0
b771ff0e  mov eax, [ecx+0x14]
b771ff11  cmp dword [eax+0x114], -0x05
b771ff18  jnz 0xb771ff24
b771ff1a  cmp dword [eax+0x110], 0xb77268e0
b771ff24  jnz 0xb7718008	->0
b771ff2a  cmp dword [eax+0x10c], -0x09
b771ff31  jnz 0xb7718008	->0
b771ff37  cmp dword [eax+0x21c], -0x05
b771ff3e  jnz 0xb771ff4a
b771ff40  cmp dword [eax+0x218], 0xb77268a8
b771ff4a  jnz 0xb7718008	->0
b771ff50  cmp dword [eax+0x214], -0x09
b771ff57  jnz 0xb7718008	->0
b771ff5d  cmp dword [eax+0x54], -0x05
b771ff61  jnz 0xb771ff6a
b771ff63  cmp dword [eax+0x50], 0xb7726910
b771ff6a  jnz 0xb7718008	->0
b771ff70  cmp dword [eax+0x4c], -0x0f
b771ff74  jnb 0xb7718008	->0
b771ff7a  movsd xmm2, [eax+0x48]
b771ff7f  xorps xmm7, xmm7
b771ff82  cvtsi2sd xmm7, edi
b771ff86  mulsd xmm7, xmm2
b771ff8a  cmp dword [eax+0x210], 0xb7726888
b771ff94  jnz 0xb7718008	->0
b771ff9a  minsd xmm7, xmm1                                ; <---- No C function call
b771ff9e  cmp dword [eax+0x108], 0xb77268c0
b771ffa8  jnz 0xb7718008	->0
b771ffae  maxsd xmm7, xmm0                                ; <---- No C function call
b771ffb2  cmp dword [edx+0xc], -0x0f
b771ffb6  jnb 0xb7718008	->0
b771ffbc  addsd xmm7, [edx+0x8]
b771ffc1  add edi, +0x01
b771ffc4  cmp edi, 0x3e8
b771ffca  jg 0xb771800c	->1
->LOOP:
b771ffd0  xorps xmm6, xmm6
b771ffd3  cvtsi2sd xmm6, edi
b771ffd7  mulsd xmm6, xmm2
b771ffdb  minsd xmm6, xmm1                                ; <---- No C function call
b771ffdf  maxsd xmm6, xmm0                                ; <---- No C function call
b771ffe3  addsd xmm7, xmm6
b771ffe7  add edi, +0x01
b771ffea  cmp edi, 0x3e8
b771fff0  jle 0xb771ffd0	->LOOP
b771fff2  jmp 0xb7718014	->3
---- TRACE 1 stop -> loop

1
It doesn't use C and is not documented to use C. coffeecat is right, it's undefined behaviour.
User avatar
raidho36
Party member
Posts: 2063
Joined: Mon Jun 17, 2013 12:00 pm

Re: Equivalent to Gamemaker's sign() function?

Post by raidho36 »

LuaJIT had produced optimized code, of course there's no C function call. Normal C code doesn't produce a function call either. But I was referring to the fact that Lua is expressly designed to work on top of C standard library, so there's no reason it shouldn't follow the same behaviors in same situations. I do however see where the bug might come from: it uses SSE instructions and they propagate 2nd argument if either of them is NaN. So under specific conditions it rearranges argument order, which results in NaNs propagating upwards from min/max functions in some situations but not in others. Note that in all of my code, possible NaNs are first argument.
Last edited by raidho36 on Mon Feb 12, 2018 11:38 am, edited 1 time in total.
User avatar
pgimeno
Party member
Posts: 3548
Joined: Sun Oct 18, 2015 2:58 pm

Re: Equivalent to Gamemaker's sign() function?

Post by pgimeno »

Undefined behaviour is not the same as bug. It's not a bug.

Implementation-defined behaviour requires compilers to stick to a behaviour. Undefined behaviour doesn't, and can change depending on the convenience of optimization.
coffeecat
Prole
Posts: 29
Joined: Sun Sep 13, 2015 4:10 pm

Re: Equivalent to Gamemaker's sign() function?

Post by coffeecat »

I know it's JIT bug and not interpreter/glibc/hardware bug because it runs fine in interpreted mode, as well as in compiled C.
If you truly think it's a bug, seriously you should report it to LuaJIT.
Lua is expressly designed to work on top of C standard library
Is this documented somewhere? And this doesn't mean Lua isn't free to choose whatever semantics it sees fit.
User avatar
raidho36
Party member
Posts: 2063
Joined: Mon Jun 17, 2013 12:00 pm

Re: Equivalent to Gamemaker's sign() function?

Post by raidho36 »

I guess it has to do with SSE and SSE doesn't define what happens if any argument is NaN. Intel and AMD simply propagate the second argument, as far as min and max goes. I did send a report but I don't suppose it's so much of a bug as documentation oversight: it works exactly one way 99% of the time to the point people don't know it can work the other way, and the docs never mention that it's actually undefined and it could happen.
Post Reply

Who is online

Users browsing this forum: Google [Bot] and 26 guests