Logical Bit Operators in Lua When Using Floats

Mon 27 February 2012 Lua

I’m making some changes to the codebase that drives Max Astro before making my next game. The biggest change is that the engine is going to be controlled by Lua. Lua will run the main logic loop and lua will be the configuration language.

One of the oddities of Lua, at least from a game makers perspective, is that there are no bit logical operators (AND, OR, XOR). The reason for this is that Lua doesn’t have an internal representation of an Integer. Lua, by default uses doubles for all numbers. There are of course a number of add on libraries to get around this.

In luaconf.h there is an option to define LUA_NUMBER as a float and I imagine most game makes do this at least on mobile platforms… and this breaks most bitop libraries for Lua. I say most because it broke the few I tried before just modifying one and moving on.

Making it Work

My library of choice is BitLib which works with Lua 5.0/5.1. Like all the libraries it’s small and easy to embed in your engine code.

Download the source code unpack it and copy bit_limits.h and lbitlib.c to your project.

Open up bit_limits.h and change it to this

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#if defined(LUA_NUMBER_FLOAT)
#   define BITLIB_FLOAT_BITS 24
#   define BITLIB_FLOAT_MAX  0x7FFFFF
#   define BITLIB_FLOAT_MIN  (-0x800000L)
#   define BITLIB_FLOAT_UMAX 0xffffff
#elif defined(LUA_NUMBER_DOUBLE)
#   define BITLIB_FLOAT_BITS 53
#   define BITLIB_FLOAT_MAX  0xfffffffffffffL
#   define BITLIB_FLOAT_MIN  (-0x10000000000000L)
#   define BITLIB_FLOAT_UMAX 0x1fffffffffffffUL
#else
#   error("Need to define float or double for numbers")
#endif

If you understand how floats are represented then all I’ve done is twiddle some bits in a calculator to work out the limits of the significand (mantissa to old people like me).

Lua Coders Know Your Limits

The downside of using floats in this case is that for each number you have, you only have 24 bits to toggle on and off. This is the number of bits in the significand (mantissa).

Now you can do

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
local COLLISION = bit.lshift(1, 0)
local WALL = bit.lshift(1, 1)
local PLAYER = bit.lshift(1, 2)
local CRATE = bit.lshift(1, 3) 
--annoyingly we can't test with truth values here
local flags = 1
assert(bit.band(flags, COLLISION) ~= 0)
assert(bit.band(flags, WALL) == 0) 
flags = bit.bor(COLLISION, CRATE)
assert(bit.band(flags, COLLISION) ~= 0)
assert(bit.band(flags, CRATE) ~= 0)
assert(bit.band(flags, PLAYER) == 0)

Much thanks has to go out to Reuben Thomas for releasing this library under a BSD license allowing me to embed this code without having to read a tome of legalese.

Hope this helps someone out there twiddle their bits.

be seeing you

dazza