Skip to content

Internal deterministic math.#626

Draft
FireBrandMint wants to merge 19 commits into
blazium-games:blazium-devfrom
FireBrandMint:blazium-dev
Draft

Internal deterministic math.#626
FireBrandMint wants to merge 19 commits into
blazium-games:blazium-devfrom
FireBrandMint:blazium-dev

Conversation

@FireBrandMint

@FireBrandMint FireBrandMint commented Apr 2, 2026

Copy link
Copy Markdown

Adds an internal number (still not available in the editor) called FInt, it does deterministic math, AKA math that produces the exact same results in any computer given the same input.

"Why?" -> All netcode has been suffering for decades with lack of determinism, I hope somebody uses this while I'm gone, please. Without determinism many things are impossible or difficult, such as: letting the server validate the client's game state with a hash - skips many expensive operations, rollback netcode syncing - the float operations are different from machine to machine, lockstep syncing - same problem as rollback netcode, edge case reduction - float precision causes many edge cases, especially in physics, resulting in many permanent ifs and elses.

"How?" -> I used a Q12 number (1Q12 = 4096i), and made a number (FInt) and a vector (Vector2FI), and the operations they need to function. I also made hyper-optimized math for it in MathFI (math_funcs_deterministic .h and .cpp)

"What is the use case?" -> In any numeric operation where you want all computers to come up with the same result given the same input AND a precision of 1/4096 is tolerable, you use this number.

Cheers.

@WhalesState

Copy link
Copy Markdown

I was trying to solve a similar problem 2 days ago where floats and double precision causes an issue when counting their decimals due to how they are stored in memory, i just saw you're storing decimal precision as two Strings, so i was wondering how much memory a single class would need to be stored? and how many memory allocations can occur for precise math operations? I know it was made to solve some specific issues so it won't be used heavily, but it really makes me interested about seeing it in action and the optimizations that it can have to make it more efficient ie. creating a TinyString class to get rid of all the String overhead that is not needed for the newly added Classes.

@FireBrandMint

FireBrandMint commented Apr 2, 2026

Copy link
Copy Markdown
Author

I was trying to solve a similar problem 2 days ago where floats and double precision causes an issue when counting their decimals due to how they are stored in memory, i just saw you're storing decimal precision as two Strings, so i was wondering how much memory a single class would need to be stored? and how many memory allocations can occur for precise math operations? I know it was made to solve some specific issues so it won't be used heavily, but it really makes me interested about seeing it in action and the optimizations that it can have to make it more efficient ie. creating a TinyString class to get rid of all the String overhead that is not needed for the newly added Classes.

I don't store 2 strings, i store a long number (it is 8 bytes, see the header for fint) the code to convert the number to a string is the one that produces a string.

EDIT: sorry for not answering the second thing; there is no allocation per operation, unless you num(String) it.

@sshiiden sshiiden marked this pull request as draft April 4, 2026 08:54
@TheYellowArchitect

Copy link
Copy Markdown

As a random guy who is making online games for 2 years full-time, and I even tried making my own netcoding back-end, my question is how does this work?

"What is the use case?" -> In any numeric operation where you want all computers to come up with the same result given the same input AND a precision of 1/4096 is tolerable, you use this number.

99999+99999 will return the same result on all computers. The main problem with non-deterministic (random) values is that players are on a different state and cannot even hash-sync.

So is this for floats only? If yes, which use-cases does it cover?

@FireBrandMint

Copy link
Copy Markdown
Author

As a random guy who is making online games for 2 years full-time, and I even tried making my own netcoding back-end, my question is how does this work?

"What is the use case?" -> In any numeric operation where you want all computers to come up with the same result given the same input AND a precision of 1/4096 is tolerable, you use this number.

99999+99999 will return the same result on all computers. The main problem with non-deterministic (random) values is that players are on a different state and cannot even hash-sync.

So is this for floats only? If yes, which use-cases does it cover?

This is to replace float in a game as a conscious choice, yes. But there is some context missing and, yes, there are other uses other than determinism, let me explain everything.

WARNING, HIGH AUTISM

About determinism

While summing up numbers will return the same result in most platforms and architectures most of the time, that is not the case for square root, sine, cosine and division. There are some reasons for that, maybe one operation gets switched around at compile time because it's easy in one architecture but it's too intensive on the next, maybe it uses a instruction that behaves slightly different across architectures, or maybe the architecture uses more or less bits per operation to handle overflow, etc.
And let me be more specific about the problem of architecture (ARM vs x64 vs x86): let's say you make 2 versions of your program, an ARM and an x64 version and you want cross play (big common undertaking), if it's a fighting game that only syncs inputs and the rounds last long, or, god forbid, it has moving platforms, it may desync slightly over time due to the difference in precision... this makes it one edge case away from a full desync. Also, if the game is P2P this problem has no fix, because should the game use one of the 2 players to re-sync the match, then a hacked client could abuse such system to make the state of the game whatever it wants.
Furthermore there are some nightmare cases out there, like 2 versions of the same phone not behaving the same with sine.

However, this number does have its use cases beyond determinism.

Beyond determinism

Broad use cases:

  • Prevent big coordinates from degrading precision. (float has that, this doesn't)

  • Have larger worlds. (the limit is 2251799813685246 being a whole number or not)

  • Consistent performance in different platforms. (that is the intention, if that's not the case I will fix it)

  • No need for 'tolerance' in most operations due to floats. (like, is this almost 0? Then so and so and so.)

  • More consistent 'snapping' to whole numbers without the usual checks.

  • No more float jank.

And so on.

Niche/rare/stretch use cases:

  • Embedded systems with no float processing unit.

  • Sorting.

  • Preventing replay desync in the same machine.

@FireBrandMint

Copy link
Copy Markdown
Author

Reminder to self: rebase when told to, squash then push.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or improvement

Projects

Status: Backlog

Development

Successfully merging this pull request may close these issues.

5 participants