Log In  


I need some help creating an absolute value function that utilizes bits to work.
My current understanding is pico8 is either 32 or 16 places. I don't quite understand which value I would use here.

If someone can clarify that would be good. For the sake of explanation, lets say its 6 bit.

v=2
000 010 --binary form of 2 in 6 bit
v=-2
111 101 --binary form of -2 in 6 bit 

So my understanding is the formula is something like:

mask=v>>number_of_bits_in_v -1
v=(v+mask)^mask

where number_of_bits_in_v=6 and v=2

And the idea is in the case of v=-2 we are adding one to it..cause all the bits to shift over. So given the above formula is subracting one....I have to know whether the number is positive or negative ahead of time and use either -1 or +1? Not sure if that'll be an issue or not.

This leads to a function like:

function absolute(v,ttl_bits)
local mask=v>>ttl_bits-1
return (v+mask)^mask
end

--given pico8 is 32...bits? i would use...?
absolute(2,32) --2? only it doesn't...it gives me a 0 if v is negative, 1 if v is positive.

The goal is to make it function like the built-in abs(-2) --2
And if there is a way for this to work like it does for decimals too...like abs(-2.432) --2.432



You need to understand, @Cerb043_4, that Pico-8 uses numbers minimal of accuracy -32768 all the way up to 32768. It also has 4-digits for the decimal place so that can be anywhere from .0001 to .9999.

There is no long integer or even a decent FLOAT variable capability. These are the limits of Pico-8.

And some of the values you get back are odd to say the least.

cls()
?32768         -- -32768
?32768+.0001   -- -32768
?32768+1       -- -32767
?-32767.9999-1 --  32767.0001

If you need to count or calculate higher or lower than this and with greater accuracy, I have written code to do so:

https://www.lexaloffle.com/bbs/?pid=101378


So, here's what I got. Somehow I thought it would be trickier. It's just straight up flipping the numbers just like you said:

function bin_abs(n)
	local result=0
	if n>><15&1==1 then
		for i=1,32 do
			result+=n&1==1 and 0 or 1
			result=result<<>1
			n=n<<>1
		end
		return result
	end
	return n
end

The line if n>><15&1==1 basically does what your local mask>>bits-1 is trying to do, except you only use the amount of bits in the integer portion, which would be 16, not the full 32. What it returns is whether or not the left-most bit is a 1 or 0, which indicates negative (1) or positive (0). You then have to iterate through all the bits in the number 'v' to flip them based on this number. My method only does it if it's negative, and skips it if it's positive. I couldn't get it to work with the ^ operator, so I rotate the number's bits, use n&1 to compare its 1's digit to 1, then give it the opposite value. After rotating through all the bits, you have your flipped bits.

Btw, I use something similar to this to save/load up to 32 booleans to a single number value for savegames.

Why did you want this?

p.s. I did a little test, running ten iterations of a calculation using my binabs() method vs abs(), and the abs() one ran with less cpu load. ¯\_(ツ)


Just figured it out. Here's a much better, simpler version:

function bin_abs(n)
	return n>><15&1==1 and ~n or n
end

The ~ operator, 'bnot' does the job. If each bit in n is 0, it puts 1, and vice-versa, flipping them all, which returns the abs value.

This version runs with the same efficiency as abs(), and is probably how it's implemented.


Very good, thankyou.
The reason I wanted it was to create a potentially more efficient way of getting the absolute value of something than abs(). Then it became curiosity on how it worked and stoodup to other methods.
Speaking of which here are the four methods I know of now....and abs() is beating all of them. Checked via ctrl+p. So if anyone has any insight on how to beat abs() and or on how abs() is working, plz share.

function bin_abs(n) --ran a bit faster when written this way
	if n>><15&1==1 then n*=-1 end
	return n
end
function if_abs(n) 
	if n<0 then n*=-1 end 
	return n 
end
function sgn_abs(n)
	return n*sgn(n)
end

function _update()
	for i=1,15000 do
		abs(-1.5) --.38 .37	
		--if_abs(-1.5) --.7 .68
		--bin_abs(-1.5) --.81 .79
		--sgn_abs(-1.5) --.86 .85
	end
end

Why do I care? I'm trying to optimize my circle-circle collision function. I started with the following and that turned into the one below it. I need it to work across multiple screens so I added in the limit to avoid overflow... which uses the abs(). The main $$$ was the abs()....so I was looking for ways to improve upon it since I couldn't think of a more efficient way to create a limit to avoid number overflow when distance get too big.

function circles_overlapping(obj1,obj2) --cx/cy/r 
	local x1=obj1.cx local y1=obj1.cy local r1=obj1.r --declaring locals takes alot of power
	local x2=obj2.cx local y2=obj2.cy local r2=obj2.r

	local dx=x2-x1 local dy=y2-y1
	if (abs(dx)<=r1+r2 and abs(dy)<=r1+r2) then --arbitrary limits
	    return dx*dx+dy*dy<=(r1+r2)*(r1+r2)
	end 
end

function circles_overlapping(obj1,obj2) --cx/cy/r; OPTIMIZED: .53 and .75 vs the original's .69 and .8; per 3800 loops 
	if (abs(obj2.cx-obj1.cx)<=obj1.r+obj2.r and abs(obj2.cy-obj1.cy)<=obj1.r+obj2.r) then --arbitrary limits
	    return (obj2.cx-obj1.cx)*(obj2.cx-obj1.cx)+(obj2.cy-obj1.cy)*(obj2.cy-obj1.cy)<=(obj1.r+obj2.r)*(obj1.r+obj2.r)
	end
end

In its usage I collide less than I check for collisions...so the expense is greatest when it makes it past the if statement...second greatest when it gets stuck on the second part of the if statement (obj 1 is below/above obj2)...and then when it's anywhere else its stuck on that first abs('dx') part. So if there's a cheaper check to incase the dx part in that would help as well. The main issue is I have so many entries that any way to cutback on the check 'cost of most' at any given time would have the greatest effect. There is some stuff outside the function that can be done in terms of when it runs, but I was focusing on what I can do within the function here.



[Please log in to post a comment]