Log In  


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



2

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"


2

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


1

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.


1

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]