This is to do with the so-called "outpost" value, or the two's-complement wrap point, which in PICO-8's number system is 0x8000, or when printed as signed decimal, -32768.
Like the origin, 0x0000, it's technically not really signed, or maybe it's better to say it covers both signs simultaneously. Conventions assign the origin to the positive side, and the outpost to the negative side, but that's just a pragmatic choice for numbers that unavoidably behave strangely due to the encoding. The origin and the outpost are complementary values in this way.
(This is why 0 == -0 and -(-32768) == -32768 with a 16-bit signed integer, by the way.)
That being said, this is what I currently get for the outpost value:
> print(tostr(abs(-32768),true)) 0x0000.0001
Obviously, there's no "good" result when we can't represent +32768 with a signed 16-bit integer.
But this result is just plain wrong in every way, because it has no connection to anything someone might expect to get as an alternative to the impossible correct result. It's not equal to -(-32768), it's not close to +32768, and it's not even exactly 0.
Technically, I think it should return 0x8000 a.k.a. -32768, just like -0x8000 a.k.a. -(-32768) does. This way, -abs(-32768) returns what it should, which matters in some cases. This is basically in keeping with the usual properties of the outpost value.
An option, albeit one I don't approve of because it's not symmetrical if you try to undo it, is to return 0x7fff.ffff, or not quite 32768. This would make the one rare edge case where someone was pushing numeric limits on PICO-8 work almost correctly, but honestly I think any calculation with a number this size is still going to fail and really ought to be done differently, so it's better if it fails obviously. Holding the hand of a new programmer in a rare edge case isn't worth breaking the behavior a seasoned one would expect in the same edge case.
Another option would be to return exactly 0, rather than slightly more than 0, to make the "bad input" situation more clear to the dev so they can fix it, because a dev trying to work with abs(0x8000) is not going to have a good day anyway and they should probably find out as soon as possible. This feels like a sketchy idea, though. And it brings us to the final option....
- The most draconian option would be to trigger a runtime error, since it's literally impossible to represent the result. I'm not sure how I feel about this. It'd definitely help devs find problems with their math, but there's always the chance it won't happen to the dev during development and will happen to the user in the wild, which is not okay. A glitch in the wild is bad but tolerable, a crash isn't.
Anyway, as you can probably tell, I think abs(0x8000) should just return 0x8000, which means in layspeak that abs(-32768) returns -32768. That may (and should) feel nonsensical to a junior programmer, but everyone needs to learn the weird rules about the outpost value eventually anyway, so I think it's the real pragmatic choice.
/me steps off of soapbox
Thanks @Felice -- this bug was introduced in 0.2.2 by using the wrong calling convention (abs() is now implemented as a "superlight C function" that is supposed to return the single numerical value, but in this case was returning the the number of Lua return values: 1).
Earlier versions of PICO-8 gave 0x7fff.ffff for abs(0x8000), which is what I've reverted to for 0.2.2d. Until v0.1.12, abs(0x8000) did actually give 0x8000, but this showed up in at least one bug report: something about measuring large distances if I recall (couldn't find it -- maybe via twitter DM or something). I'm partial to the argument for 0x8000, but I do like the property that abs() always returns a positive number; for example sqrt(abs(0x8000)) might be confusing otherwise.
Thanks, @zep. I'm not totally against 0x7fff.ffff. No matter what you do, someone's always not going to get the output they want when they give invalid input. It's a decent compromise choice that'll work in most cases, and people who care can always check manually.
[Please log in to post a comment]