Let's take a look at how to work with decimal numbers on an ancient cpu.

Modern computers work with decimals, like `0.5`

, using a system called floating point. If you remember scientific notation from your science classes, it's based on the same idea. These types of numbers are handled by the cpu in modern computers, and so programs can calculate with them very quickly. The Neo Geo can not work with decimals at all. It only knows how to calculate integers, like `1+3`

, `88*4`

, or `-45 / 5`

. If you want to do `8 * 0.5`

on the Neo Geo, you just can't. But for games, you almost always need decimal numbers, so what to do?

Thankfully there is a way to get them on something as old as the Neo Geo, using a number system called fixed point.

## Floating Point

Floating point numbers are decimal numbers where the decimal itself can be in any position. This allows computers to work with small decimal numbers like `3.00000000001`

or big numbers like `12345678.2`

, by moving the decimal point around, the decimal system is much more flexible. This article won't go into the details of how floating point works. But just know that when writing a program nowadays, you typically just use decimal numbers and hardly think much about them, as they just work.

Floating point is complicated, so it's pretty slow. Modern computers deal with this by doing floating point calculations in hardware instead of software. Old systems like the Neo Geo "dealt" with this by not supporting it at all :)

## Fixed Point

In a fixed point system, the decimal point is locked in place. For example one system might use six decimal digits and 10 integer digits, like `1234567890.450012`

The advantage to this is being very simple and very fast. Behind the scenes the fixed point number is actually stored in an integer. The CPU itself has no idea it is working with fixed point, it just sees integers and can do arithmetic on them just like any other number it encounters.

The disadvantage is precision and flexibility. If you need really precise decimal numbers, you might choose your fixed point system to have 4 integer digits and 12 decimal. But that means the maximum integer in that system is only 7! If you go the opposite direction, 12 integer and 4 decimal, your maximum integer is 2047. Which gives you more breathing room, but then you're unable to form precise decimal numbers.

Where you place it just depends on what your needs are. For my game, I've opted for a system that creates pretty precise decimal numbers at the cost of smallish integers. Usually that is ok. Typically you're calculating where on the screen something will be, and since the Neo Geo's resolution is only 320x224, having smaller integers is often just fine.

## But why do you need fixed point?

This is a fair question. As a simple example, let's look at placing sprites on the screen. You can't place a sprite at `(32.84, 44.9442)`

, so what's the point of being this precise?

Pretend you have a character that can walk across the screen, like Blue here

A simple way to do that is to decide Blue moves 1 pixel every frame. So the code might be something like this

`if ` (pressingRight) { blue .x += 1;}
graphics_moveSprite(blue.spriteIndex, blue .x, blue .y);

That's fine, `1`

pixel per frame means he moves 60 pixels every second. He'd cross the screen in about 5 seconds. Ok what if we want him to move faster? `2`

pixels per frame? He'd cross the screen in about 2.5 seconds. What if that is too fast? There isn't much that can be done to deal with that. It's either too slow at `1`

pixel, or too fast at `2`

pixels. You can try fancy things like only moving him 2 out of every 3 frames. But that gets complex quick, and not to mention you're game isn't really 60fps anymore if you do that.

With fixed point, we could say that Blue moves at `1.2`

pixels per frame, or just about any fractional value we want (within reason). We can decide exactly how fast Blue moves, move him every frame, and it will all just work, nice and simple.

With our fixed point system, Blue might be at 32.2 pixels one frame, then 32.8 pixels the next. Since sprites have to be placed on the screen using integers, that might mean visually Blue is at 32 pixels for two frames, then 33 pixels on the third frame. Not ideal, but also nothing we can really do about it. That's just the reality on these old low resolution systems.

Thankfully that is only a visual limitation. The logic of your game remains precise, so your controls and the "feel" of the game remains snappy.

## Making a fixed point system

A fixed point system is pretty simple to build. First, decide which size of integer to use for fixed point. The Neo Geo can work with up to 32 bits pretty well. If you are using ngdevkit, that is `s32`

. That's what I'm using in my game and it is working well.

Then decide how many of those bits will be for the decimal portion, something like this:

`typedef `s32 fixed ;#define DECIMAL_BITS 5

Then we need some simple macros to convert to and from our fixed point numbers

`#define ` SCALE (1 << DECIMAL_BITS)#define TO_FIXED(a) ((a) * SCALE)#define FROM_FIXED(a) ((a) / SCALE)

And yeah, all we are really doing is moving our numbers up higher in the 32 bit space. That's pretty much all it takes to simulate decimals.

Now with those in place, addition and subtraction are simple to do

`// `create two integers s16 a = 5;s16 b = 6;// convert them into our fixed system fixed fa = TO_FIXED(a);fixed fb = TO_FIXED(b);// add them together fixed fc = fa + fb ;// or subtract fixed fc = fa - fb ;// convert them back into integers s16 c = FROM_FIXED(fc);

In my game, I almost always store something's `x`

and `y`

values for its location on screen as `fixed`

, then right before I need to update its sprite, I will convert it back to integers.

`struct ` Bullet { fixed xF ; fixed yF ; u16 spriteIndex ;};
struct Bullet b ;
b.xF = <<SOME FIXED CALCULATION >>b.yF = <<SOME FIXED CALCULATION >>
graphics_moveSprite(b.spriteIndex, FROM_FIXED(b.xF), FROM_FIXED(b.yF));

That code is very simplified, but you get the idea.

### Multiplication

Now things get a tad more complex. When multiplying two fixed numbers together, the "decimal point's location" will move. Basically each fixed number has `SCALE`

applied to it, so after a multiplication, the result with have `SCALE*SCALE`

applied to it, so a division is needed to bring the result back down to a single `SCALE`

.

`fixed ` fixed_multiply(fixed a , fixed b ) { fixed temp = a * b ;
// temp has been SCALE'd up twice, so bring it back down fixed result = temp / SCALE ;
return result ;}

**Hey!**don't use the code from this blog post directly. I have simplified it as this post is just about the basics of fixed point. Most notably, multiplication and division should both round their results. See the conclusion section below.

And to use it, just call `fixed_multiply`

instead of using `*`

directly

`fixed `fa = TO_FIXED(5);fixed fb = TO_FIXED(8);fixed fc = fixed_multiply(fa, fb );

You only need to use `fixed_multiply`

when both numbers are in the fixed system. Just need to double a fixed number? This will work

`fixed `fa = TO_FIXED(5);fixed doubledFa = fa * 2;

It will be a little bit faster, but mixing `*`

and `fixed_multiply`

incorrectly will lead to bugs, so if not sure or worried, just stick with `fixed_multiply`

`fixed `fa = TO_FIXED(5);fixed doubledFa = fixed_multiply(fa, TO_FIXED(2));

### Multiplication Overflow

Multiplying two 32 bit numbers together and storing the result in a 32 bit number can cause overflow. take two very big numbers. When multiplied together, the result will be gigantic. Too big to fit into 32 bits, so some of it will get thrown away, causing the result to be incorrect. The common way to avoid this problem is to make `temp`

be 64 bits.

But that is a problem on the Neo Geo, it has no 64 bit numbers!

The way I handle this is to assert the result hasn't overflown

`fixed ` fixed_multiply(fixed a , fixed b ) { fixed temp = a * b ;
ngassert( a == 0 || b == 0 || abs(temp) >= abs(a), "fixed_multiply overflow, a: %d, b: %d, temp: %d" , a , b , temp );
// temp has been SCALE'd up twice, so bring it back down fixed result = temp / SCALE ;
return result ;}

If after multiplying is finished, if `temp`

is smaller than `a`

, that is a sign the result was too big. This is not ideal, because if you have multiplication overflow there isn't anything you can do except change your game to avoid it. But this is way better than just allowing the error to happen and having a broken game.

### Division

Similarly, division needs to be a function too

` `fixed fixed_divide(fixed a , fixed b ) { fixed temp = a * SCALE ;
ngassert( a == 0 || abs(temp) >= abs(a), "fixed_divide overflow" );
return temp / b ;}

Division deals with `SCALE`

in the opposite way as multiplication. Here the result will have `SCALE`

removed from it, so first we `SCALE`

up the input a second time. After division, the result is back to the correct scale.

Just like in `fixed_multiply`

, there might not be enough room to scale up a, so we again check for that with `ngassert()`

.

Also dividing by a large number (say `1 / 1000000000`

) can result in `0`

if there isn't enough decimal precision. I should probably assert on that too...

## Fixed literals

You may have noticed I've never done `fixed fa = TO_FIXED(5.5)`

, as that isn't possible. The way to do that is unfortunately

`// `fa will be "5.5" fixed fa = TO_FIXED(5) + fixed_divide(TO_FIXED(1), TO_FIXED(2));

This is hard to read, but also `fixed_divide`

is a function, not a macro, so this initialization will happen at runtime instead of compile time. To work around this, I have a "fixedConstants.h" in my game, which makes this a little more simple

`#include `"fixedConstants.h"
fixed fa = TO_FIXED(5) + FIXED_ONE_HALF;

Easier to read, and all resolved by the compiler instead of the cpu. I wrote a little node script to generate fixedConstants.h

`import `path from 'node:path';import fsp from 'node:fs/promises';
const decimalBits = 6;const scale = 1 << decimalBits ;
function toFixed(a: number) { return Math .floor(a * scale );}
async function main(outputDir: string): Promise<void> { const rawDefines : string[] = [];
rawDefines .push(`ONE_HALF ${toFixed(1 / 2)}`); rawDefines .push(`ONE_THIRD ${toFixed(1 / 3)}`); rawDefines .push(`ONE_FOURTH ${toFixed(1 / 4)}`); rawDefines .push(`ONE_FIFTH ${toFixed(1 / 5)}`); rawDefines .push(`ONE_SIXTH ${toFixed(1 / 6)}`); rawDefines .push(`ONE_TENTH ${toFixed(1 / 10)}`); rawDefines .push(`ONE_TWENTIETH ${toFixed(1 / 20)}`); rawDefines .push(`THREE_FOURTHS ${toFixed(3 / 4)}`); rawDefines .push(`TWO_THIRDS ${toFixed(2 / 3)}`);
const defines = rawDefines .map((rd) => { return `#define FIXED_ ${rd}`; });
const src = `#pragma once ${defines.join('\n')}`;
const definesOutputPath = path .resolve(outputDir, 'fixedConstants.h'); await fsp .writeFile(definesOutputPath, src ); console.log('wrote', definesOutputPath );}
const [_node, _buildFixedConstants , outputDir ] = process .argv;
if (!outputDir) { console.error('usage: ts-node buildFixedConstants ' ); process .exit(1);}
main(path.resolve(outputDir)) .then(() => console.log('done')) .catch((e) => console.error(e));

## Changing the precision

As I worked on my game, I realized I needed to change how many bits I used for decimal and integer in my fixed point system. Just today I found I didn't have enough decimal bits and couldn't create the small fractions I needed (which inspired me to write this post).

To deal with this, I have actually defined the number of decimal bits as an environment variable. My C code, makefile, and scripts all feed off of that environment variable. So if I need to change the precision, I just need to update the variable, regenerate things like fixedConstants.h, and then do a full recompile. This is way better than hunting down every place that deals with decimal bits.

So for example, in the script above, it's actually doing this in my real version

`if ` ( process .env.NEO_DECIMAL_BITS === undefined || Number .isNaN(parseInt(process.env.NEO_DECIMAL_BITS))) { throw new Error('NEO_DECIMAL_BITS env variable was not set' );}
const decimalBits = parseInt(process.env.NEO_DECIMAL_BITS);const scale = 1 << decimalBits ;
function toFixed(a: number) { return Math .floor(a * scale );}

## Simulating floating point with GCC

As a side note, if you are using ngdevkit, then you can using floating point numbers like `float`

and even `double`

. The catch is, GCC will create a software floating point implementation. It's *horrifically* slow, especially if you try to use `double`

. This almost certainly can't be used in a real game. But it can be used in a pinch when just trying something or making a quick demo. It's good the option is there.

To use it, just add a `float`

number to your code like you would when writing for a modern system. GCC will do the rest. One way I used it was to help confirm the fixed point system I created was accurate. I'd do some calculations in my system as well as GCC's floating point system and confirm both got the same result.

## Conclusion

This post was really just a basic introduction to fixed point. I am far from an expert on the subject. In fact, I'd never used a fixed point system until I started hacking on the Neo Geo. I recommend more detailed reading such as this article before adding a fixed point system to your game.

You typically need to create your own fixed point system on something like the Neo Geo instead of using a library because most libraries make assumptions that won't hold true on on old game systems. Mostly they will promote multiplication and division to 64 bit numbers which isn't really feasable.