Hi Guys, i found a bug? not sure if this -0 (negative zero) is a feature or somewhat useful on other scenarios, kindly try out this code
x = 1 t = -100 function _update() cls(13) t*=0.5 color(7) print("x+t:"..x+t) print("x:"..x) print("t:"..t) circfill(60,10,5,x+t) circfill(60,30,5,x) end |
im using t to animate colors or sprites but i noticed that when t is approaching to 0 from a negative value, it stays negative even tho its 0, and i thought, "of course it does not matter because it's still zero" but it does matter because somehow it's 1 off to 0, at least that's what i think happens base on the colors
im doing a workaround where i check if it's less than 0 and set it to 0. but it costs tokens :<<< i hope this gets resolved. thanks
Yeah, it looks like T is not actually 0 or -0 but something between -0.00003 and -0.000016:
print(tostr(-0.00003,0x1)) print(tostr(-0.000016,0x1)) print(tostr(t,0x1)) |
Each of those returns 0xffff.ffff when you get to the point in your code that your print shows t = -0.
The smarter people here probably have a better idea, but this is probably you bumping up against the precision limitations in PICO-8. I think once your halving operation gets below 0x0.000016 it just stops doing anything maybe and you get stuck at -0xffff.ffff instead of ever getting to 0? The manual says:
"Numbers in PICO-8 are all 16:16 fixed point. They range from -32768.0 to 32767.99999"
No bug, just fixed floating point weirdness :
when t is at 0xffff.ffff with is the biggest (nearest to zero) negative value a 16.16bits variable can hold, printing it shows -0 witch is a very accurate if weird looking approximation in base ten.
when you do t*=0.5, the result is exactly the mid point between
0xffff.ffff and 0x0000.0000, so a rounding to any of those two values is valid.
If you want to reach 0 without using extra tokens, you can use division instead of multiplication to get the rounding towards zero.
t*=0.5 becomes t/=2
0xffff.ffff/2 gives 0x0000.0000
0xffff.ffff*0.5 gives 0xffff.ffff
oohhhhh. Thanks 2bitchuck and RealShadowCaster for these infos, really appreciate it.
I tried t/=2 but something new is happening.
there is a bit of delay on x-t but for x+t there is no delay,
x = 1 t = 100 -- i changed t to 100 instead of -100 function _update() cls(13) t/=2 color(7) print("x+t:"..x+t) print("x-t:"..x-t) print("x:"..x) print("t:"..t) circfill(60,10,5,x+t) circfill(60,20,5,x-t) circfill(60,50,5,x) end |
what i did was to use t\=2 to just floor it hahahaha. either way thank you so much guys, I learned something new today especially with those weird things in floating point
The delay is just because of adding vs subtracting.
For instance, consider what happens when t=1. Then
x+t = 2, and
x-t = 0
As t gets smaller and closer to 0 both of those values approach 1 but from different directions. When t=0.9:
x+t = 1.9, and
x-t=0.1.
As colours those will just be considered as 1 and 0 respectively. So when t becomes less than one, x+t goes immediately to colour 1. But x-t won't go to colour 1 until the very end when t=0. Hence the delay.
If you want symmetrical signed behavior, you can do this to circumvent the 2's complement hazard with negative rounding behavior:
-- long form if t<0 then t = -(-t*.5) else t = t*.5 end -- shorthand t = t<0 and -(-t*.5) or t*.5 |
This will always trend towards true 0.
This is why true IEEE-style floating point numbers, unlike PICO-8's signed 2's-complement 16.16 fixed-point numbers, don't actually use 2's complement to represent the mantissa. The mantissa is always positive and there is a separate bit flag to say if the mantissa should be treated as negative.
As an aside, you could also do >>1
instead of *.5
. Not a very useful optimization for something you only do once per frame, but hey, worth knowing at least, maybe for other use cases. 🙂
[Please log in to post a comment]