From 4b37aedb4136e9e51d0b39a42105863cc959dcb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antonio=20Ni=C3=B1o=20D=C3=ADaz?= Date: Sun, 24 Mar 2024 18:49:08 +0100 Subject: [PATCH] Add explanation of converting between floating and fixed point (#21) * Add explanation of converting between floating and fixed point This is useful in many situations, like when generating fixed point constants, or when printing fixed point values for debugging (or non debugging) purposes. Also, fix some qualifiers (`extern` isn't needed, `const` is nice to use). * Update content/fixed-point-math.md Co-authored-by: Antonio Vivace * Add suggestion --------- Co-authored-by: Antonio Vivace --- content/fixed-point-math.md | 59 +++++++++++++++++++++++++++++++++---- 1 file changed, 54 insertions(+), 5 deletions(-) diff --git a/content/fixed-point-math.md b/content/fixed-point-math.md index cee02b9..7799324 100644 --- a/content/fixed-point-math.md +++ b/content/fixed-point-math.md @@ -111,13 +111,13 @@ Let's say we have a player sprite, with an X and Y position. What speed should w ```c // a function elsewhere in code, which needs the ONSCREEN x and y position -extern void display_player(int x, int y); +void display_player(int x, int y); // player coordinates use signed 28.3f fixed-point format -int player_fp = 3; +const int player_fp = 3; // 1 and 5/8, written verbosely for demonstration purposes -int player_speed = 8 + 5; +int player_speed = (1 << player_fp) + ((5 << player_fp) / 8); // let's start the player at (5, 6) on the screen // shift both of these values left to account for the fixed-point @@ -163,10 +163,11 @@ Before adding or subtracting fixed-point numbers, you need to make the denominat ```c // variable 'a' uses an unsigned 27.5f fixed-point format -int a_fp = 5; +const int a_fp = 5; unsigned int a = 3 << a_fp; + // variable 'b' uses an unsigned 16.16f fixed-point format -int b_fp = 16; +const int b_fp = 16; unsigned int b = 4 << b_fp; ``` @@ -206,6 +207,54 @@ Even though you can't do hardware-level division, there are usually creative wor If you really must divide, you would multiply the numerator by the fixed-point first, *before* dividing. +## Converting between fixed and floating point + +Now you have a way to do mathematical operations efficiently. How do you set the initial values in a convenient way? How do you print the values in a way that is easier to understand than very big integer values? + +Well, you can convert between fixed and floating point easily: + +```c +const int player_fp = 3; + +static inline int player_float2fixed(float value) +{ + return (int)(value * (1 << player_fp)); +} + +static inline float player_fixed2float(int value) +{ + return value / (float)(1 << player_fp); +} + +// Macro version of the functions, for situations where you can't use functions +#define PLAYER_FLOAT2FIXED(value) (int)((value) * (1 << (player_fp))) +#define PLAYER_FIXED2FLOAT(value) ((value) / (float)(1 << (player_fp))) + +int setup(void) +{ + int player_x = player_float2fixed(1.23); + int player_y = PLAYER_FLOAT2FIXED(2.35); + + printf("Player X: %f\n", player_fixed2float(player_x); + printf("Player Y: %f\n", PLAYER_FIXED2FLOAT(player_y); +} +``` + +Remember that those are floating point operations, so they will be slow. There is an exception: if you use `constexpr` or if the compiler detects that an expression is constant, it will calculate it at compile time automatically. This is very useful for setting initial fixed point values from floating point values. + +```c +int player_x, player_y; + +constexpr int player_start_x = player_float2fixed(1.23); // Only in C++ +const int player_start_y = PLAYER_FLOAT2FIXED(2.35); + +int setup(void) +{ + player_x = player_start_x; + player_y = player_start_y; +} +``` + And there you go! You now know everything needed to do fixed-point math. Good luck! ## FAQ