Log In  


hey is it just me or is sgn(0) returning 1 where it should return 0?
i get this with both literal 0 and 0 through arithmetic

2



ah. i guess i shouldn't hold out much hope for a fix soon then lol


I've seen both sgn(0)==1 and sgn(0)==0 in various code bases, and also less ambiguous versions: sgn0(), sgn2(), sgn3() that return 3, 2 and 3 possible values respectively. After a quick survey, it looks like sgn(0)==0 is more typical and maybe has some mathematical basis (?). It's still not too late to change the behaviour in pico-8 I think.

Just out of curiosity, anyone care to give an example of when sgn(0)==0 is more useful? I generally use it in situations where only a non-zero result is meaningful. e.g. player.x + sgn(player.dx) * player.w to get the point in front of a player.


A common example of sgn(0)==0 would be in collision. The standard grid raycast loop uses it, since you don't want to step if you're moving zero in x or y:

sx = sgn(dx) sy = sgn(dy)
gx = flr(x/scale) gy = flr(y/scale)
curt = 0
dxt = sx*scale/dx dyt = sy*scale/dy #full grid step in t, div by 0=max in lua?
nxt = sx == 0 and 2 or ((scale*gx+.5*scale*(sx+1)) - x) / dx #first x in t
nyt = sy == 0 and 2 or ((scale*gy+.5*scale*(sy+1)) - y) / dy #first y in t
while(curt < 1) do
    if(nxt < nyt) then curt=nxt gx+=sx nxt+=dxt
    else curt=nyt gy+=sy nyt+=dyt end
    #do something at grid position (gx,gy)
end

I can't really think of other places where sign(x) is used offhand, it's kind of a rare function. Also that code is from memory, so don't trust me on it. =)


There was a sign function?! D: And I've been rolling my own! Gah.


Hey! I poked you Zep on twitter about this without realizing it was already being discussed.

sgn(0) = 0 is definitely the more common one in my experience; Flashpunk uses it in its utility functions, as does the C# standard Math library. Since zero is signless (usually), it doesn't seem correct to me for it to return 1.

I was planning to use this recently for an input system, making use of the new input bitfield like so:

local keys = btn()
x += sgn(band(keys, 2)) - sgn(band(keys, 1))
y += sgn(band(keys, 8)) - sgn(band(keys, 4))

Versus how I usually do it:

if btn(0) then
	x -= 1
end
if btn(1) then
	x += 1
end
if btn(2) then
	y -= 1
end
if btn(3) then
	y += 1
end

I've just now tested it and apparently I'm not actually saving on tokens, haha. Maybe I don't want this feature after all ;)


In terms of saving tokens or writing elegant code, I think sgn(0)==0 vs. sgn(0) == 1 is much of a muchness. More important is that it behaves as you'd expect it to without reading the manual (when there is an entry!), and it seems most users expect sgn(0) == 0. I'll see about changing it to return 0 in 0.1.5 if there's a way to do it without breaking carts in the wild too much.


1

@jacobalbano @zep

I dug up this thread!

I would expect FALSE to return 0 to make more sense for use.

Cart #sgn_control-0 | 2023-04-25 | Code ▽ | Embed ▽ | No License
1

--** 32 tokens **--
if btn(0) then
 x -= 1
end
if btn(1) then
 x += 1
end
if btn(2) then
 y -= 1
end
if btn(3) then
 y += 1
end

--** 30 tokens **--
x += btn(0) and -1 or btn(1) and 1 or 0
y += btn(2) and -1 or btn(3) and 1 or 0

--** 26 tokens [use sgn()!] **--
x += sgn(btn(1)) - sgn(btn(0))
y += sgn(btn(3)) - sgn(btn(2))
--** Wrapper function with FALSE returning 0 **--

_sgn,sgn=sgn,function(v)
 return v == false and 0 or _sgn(v)
end

1

@shiftalow @zep

Interesting idea. Not sure if, mathematically, it makes sense for false to be negative. False is usually 0. Mind you, false is semantically synonymous with negative in spoken language, and 0 being "positive" is just an arbitrary choice in two's-complement notation.

Setting that quibble aside, if one wants to convert a boolean to 0/1 they can already do it with tonum(bool), so maybe letting sgn(bool) produce -1,+1 would be a good feature, since it provides the extra functionality your examples demonstrate, whereas the current functionality isn't really functionality at all.


1

In my initial post I was expecting 0 to be output to result in a sgn(false) suitable for btn().

However, I noticed in @Felice's reply the ability to numerically convert bool to 1,0 with tonum()!
I am now convinced that sgn(bool) outputs +1,-1.

We will be able to choose between tonum() which outputs 1,0 and sgn()` which outputs 1,-1! (*1 

Thanks!

(*1 Correction of text

Since I can choose between tonum(), which outputs 1 and 0, and sgn(), which outputs 1 and -1, I am now convinced that sgn(bool) outputs +1,-1!


1

@shiftalow

Ah! Right, I actually misread your code, so yes, all you needed was for tonum() to do what you were asking for sgn() to do.

This is all good to know, by the way! I only learned that tonum() handled booleans because of this thread. It's definitely a great way to handle dpad input.

Hm, I should update the wiki if it doesn't already mention this. Oh, it already does. Silly me. Didn't see it, though, since the note was buried in the long text. I edited it to be more clear.



[Please log in to post a comment]