What I would like to know is if there exists a method to calculate difficulty without using floating point math, and what that method is.
But difficulty is a floating point number, so I don't know how you can get there without doing floating point math. The most straightforward way I know is simply what is in the client source code:
i = binary_to_int(b)
nShift = (i >> 24) & 0xff
dDiff = float(0x0000ffff) / float(i & 0x00ffffff)
while nShift < 29:
dDiff *= 256.0
nShift += 1
while nShift > 29:
dDiff /= 256.0
nShift -= 1
Even I don't totally understand the calculation, but I think this is as simple as it gets, using some bit-math (shiftings, bit-and). Be aware that the difficulty is typically a 32-bit number that looks like 1DAABBCC. The first 8 bits (1D) are the "nShift" above, and the last 24 bits (AABBCC) contribute to the dDiff number. It looks like to me that the "nShift" is the raw number of zero-bytes needed on the front of the hash (multiples of eight 0-bits), and the other 24 bits give you an adjustment factor. Something like that...
The way I calculate the longest chain is to start with a pool of unorganized blocks, accessed by hash. I go through all the blocks one-by-one, and follow them down to the genesis block (or the first block that I've already calculated) by following the "previous-hash" field, accumulating all the difficulty values as I go. When I get to the "end", I walk back up the chain in the exact same order, and accumulate the difficulty-sums assigning them to each block as I go. After that, I just find the block with the highest sum.
The reason I can't just start at the genesis block and walk up is that "next-hash" is not initially defined. And if the block chain forks anywhere, you would actually have two "next-hash" values, and you won't know which branch to follow without knowing the longest chain... but that's what we're trying to find!
But of course, you're going to need that difficulty calculation above to make it work.