Fixed point

Normally you'll be working with integer numbers (1, -25, 123, etc.), but sometimes you'll want to be able to use in-between values (for example, measuring speed in whole pixels is a bad idea because then you'll be stuck with multiples of 60px per second!).

Modern CPUs use floating point to handle non-integer numbers, but that's too slow on the 68000. Instead, we'll learn of another approach that's much faster on old hardware, called fixed point.

What's fixed point?

The idea behind fixed point is that instead of 1 = one unit, you use a larger number to mean one unit, and smaller numbers refer to sub-unit amounts. For example, if 1000 = 1 unit, then 250 = 0.25 units (because 1000 × 0.25 = 250).

The most common fixed point format on 68000 is "16.16" (i.e. 16-bit integer part, 16-bit fractional part). This is because it allows for some optimizations taking advantage of the 68000's ability to handle both 16-bit and 32-bit values. In this case, one unit = 65536 (or $10000 in hexadecimal).

Iwis says

The name fixed point comes from the fact that the "decimal point" is always in the same place (contrast with floating point where the precision of the fractional part changes depending how large is the number).

Basic math

The following operations are exactly the same for integers and for fixed point, so keep using them the same way you were already doing:

This remains true for both signed and unsigned, as well.

Converting to integer

To get the integer part of a fixed point number, you divide the value by one unit… normally, you'll make it so that 1 unit is a power of two, which means you'll be able to do a bit shift right instead.

If you followed the suggestion to use 16-bit integer and fractional parts, it's even easier:

The above not only is easier to program for, but also it's faster than doing bit shifts.

Iwis says

In many cases you'll want to stick to the integer part for things where sub-unit precision isn't that important, e.g. checking collision between two objects (where pixel precision is just fine).

Rounding to nearest integer

The method mentioned above always rounds down, i.e. towards negative. This is best for performance and in many cases it's good enough for what you want (or outright what you actually need), but maybe you'd prefer to round towards the nearest whole number instead.

To do this, take the original number, add 0.5 units, then strip down the fractional part (following the format above, add 32768 and then drop the bottom 16 bits).

Multiplication and division

Multiplication and division become more problematic, because they cause the location of the decimal point to shift! Ideally you should try to avoid them, but if you can't:

If you're using the 16-bit integer + 16-bit fractional part format, then you can divide by one unit by doing a bit shift right by 16. This said, you'll be still dealing with large multiplications and divisions (which are harder and much slower to do on the 68000), so you really want still want to avoid this unless absolutely needed.

Also incidentally, if the second operand (i.e. B) is integer, it becomes simpler since you can skip the "divide by one unit" step in both cases. In that case use B as-is (i.e. without converting to fixed point).