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]