8000 bugfix(player): Fix rounding inaccuracies with money awarded by Cash Bounty by Stubbjax · Pull Request #2330 · TheSuperHackers/GeneralsGameCode · GitHub
[go: up one dir, main page]

Skip to content

bugfix(player): Fix rounding inaccuracies with money awarded by Cash Bounty#2330

Merged
xezon merged 2 commits intoTheSuperHackers:mainfrom
Stubbjax:fix-cash-bounty-rounding
Feb 21, 2026
Merged

bugfix(player): Fix rounding inaccuracies with money awarded by Cash Bounty#2330
xezon merged 2 commits intoTheSuperHackers:mainfrom
Stubbjax:fix-cash-bounty-rounding

Conversation

@Stubbjax
Copy link

Closes #251

This change fixes an issue with the money awarded by Cash Bounty, where particular percentage calculations can cause a rounding error and return the incorrect amount.

For example, a Cash Bounty value of 26% causes an object with a value of $1,000 to unexpectedly yield $261. This is because calculations like 1000 × 0.26 can effectively return 260.0000000001 due to floating point inaccuracies, which is then rounded up.

@Stubbjax Stubbjax self-assigned this Feb 20, 2026
@Stubbjax Stubbjax added Bug Something is not working right, typically is user facing GLA Affects GLA faction Minor Severity: Minor < Major < Critical < Blocker Gen Relates to Generals ZH Relates to Zero Hour NoRetail This fix or change is not applicable with Retail game compatibility labels Feb 20, 2026
@greptile-apps
Copy link
greptile-apps bot commented Feb 20, 2026

Greptile Summary

Fixed floating-point rounding inaccuracies in the Cash Bounty calculation that caused values like 26% of $1,000 to incorrectly yield $261 instead of $260.

  • Subtracts WWMATH_EPSILON before ceiling to handle floating-point representation errors
  • Replaces REAL_TO_INT_CEIL macro with standard ceil function to avoid unpredictable CPU rounding mode behavior
  • Applied consistently to both Generals and GeneralsMD codebases
  • Guarded behind RETAIL_COMPATIBLE_CRC to preserve original behavior for compatibility

Confidence Score: 5/5

  • This PR is safe to merge with minimal risk
  • The fix addresses a well-documented floating-point precision issue with a standard epsilon subtraction technique. The changes are minimal, identical across both codebases, properly guarded for compatibility, and use safer standard library functions instead of platform-specific macros with unpredictable behavior.
  • No files require special attention

Important Files Changed

Filename Overview
Generals/Code/GameEngine/Source/Common/RTS/Player.cpp Fixed floating-point rounding error in cash bounty calculation by subtracting epsilon before ceiling and replacing macro with standard ceil function
GeneralsMD/Code/GameEngine/Source/Common/RTS/Player.cpp Fixed floating-point rounding error in cash bounty calculation by subtracting epsilon before ceiling and replacing macro with standard ceil function

Last reviewed commit: 340d2bb

@Caball009
Copy link

Is there a point to fixing this particular issue instead of fixing the fast_float_ceil function itself? That REAL_TO_INT_CEIL(1.0f) == 2 is rather unfortunate.

@Stubbjax
Copy link
Author

Is there a point to fixing this particular issue instead of fixing the fast_float_ceil function itself? That REAL_TO_INT_CEIL(1.0f) == 2 is rather unfortunate.

Ideally yes, but given its extensive usage across the code base, there could be many unforeseen/unpredictable consequences.

@DrGoldFish1
Copy link

You always get +1$ no matter what unit you kill. When you have cashbounty.

@Mauller
Copy link
Mauller commented Feb 20, 2026

Does using the standard math ceil function correct the behaviour?

@Caball009
Copy link

Does using the standard math ceil function correct the behaviour?

Yes.

< 8000 /div>
@Mauller
Copy link
Mauller commented Feb 20, 2026

For the non retail path it might be better to just use the standard ceil function instead of correcting the macro version.

we want to move away from those in non retail code.

@Caball009
Copy link
Caball009 commented Feb 20, 2026

For the non retail path it might be better to just use the standard ceil function instead of correcting the macro version.

We'll need to use math functions that produce the same output with different compilers / architectures if possible.

For example, std::cbrt(27.0) == 3.0 is not necessarily true for all compilers (this is without fast math of course), or for the same compiler at different optimization levels. More shenangians here: https://isocpp.org/wiki/faq/newbie#floating-point-arith2

@xezon
Copy link
xezon commented Feb 20, 2026

We'll need to use math functions that produce the same output with different compilers / architectures if possible.

Yes but that is no concern right now because we are not yet multi platform.

@Caball009

By the time we break retail compatibility it'll be relevant, though, which is when this applies, anyway.

@bobtista
Copy link

Is there a point to fixing this particular issue instead of fixing the fast_float_ceil function itself? That REAL_TO_INT_CEIL(1.0f) == 2 is rather unfortunate.

Ideally yes, but given its extensive usage across the code base, there could be many unforeseen/unpredictable consequences.

I think we can safely change the GameClient calls without worrying about mismatches, but GameLogic ones will probably need to be behind flags. I think it'd be worth while to fix fast_float_ceil and see if anything noticeably breaks. While it wouldn't surprise me to see things that depend on an off by one bug to work correctly here, it could be fine - we should just test it. To be fair, the risk reward isn't huge here, in terms of where we should be spending our time. I just like the idea of fixing an off by one bug

@Skyaero42
Copy link
Skyaero42 commented Feb 21, 2026

I think there is a broader issue here: always rounding up - whether it is due to floating point inaccuracy or just decimal math. A better way would be to do proper rounding (I know, difficult in VC6).

As it involves money, one could argue that the best way is actually bankers rounding (round to even number). Further along, one could also argue using fixed decimal point types (I don't know if they exists in C++) rather then using floating point types.

As any change is non-retail compatible, in my opinion any change to the rounding should immediately work towards a permanent solution rather then using a epsilon hack.

@Stubbjax
Copy link
Author

As it involves money, one could argue that the best way is actually bankers rounding (round to even number). Further along, one could also argue using fixed decimal point types (I don't know if they exists in C++) rather then using floating point types.

As any change is non-retail compatible, in my opinion any change to the rounding should immediately work towards a permanent solution rather then using a epsilon hack.

I agree, and would have originally opted to use floor over ceil, but changing the rounding would be more of a design change and I intended to keep this change explicit.

@xezon xezon changed the title bugfix: Fix rounding inaccuracies with money awarded by Cash Bounty bugfix(player): Fix rounding inaccuracies with money awarded by Cash Bounty Feb 21, 2026
@xezon xezon merged commit 09f3a84 into TheSuperHackers:main Feb 21, 2026
25 checks passed
@Caball009
Copy link

Did regular ceil still need the epsilon offset?

@xezon
Copy link
xezon commented Feb 21, 2026

Probably, because 260.0000000001 is above 260.

@Caball009
Copy link

That wasn't the issue to begin with, and to answer my own question ceil(1000 * 0.26f) == 260, so the epsilon seems unnecessary.

@xezon
Copy link
xezon commented Feb 21, 2026

Oh. Can make a follow up change :)

@Stubbjax
Copy link
Author

Did regular ceil still need the epsilon offset?

Yes - it guarantees calculations are always correct. Ceil does not eliminate floating point inaccuracies. 0.26 × 1,000 may work, but 4.01 × 1000 = 4,011.

@Stubbjax Stubbjax deleted the fix-cash-bounty-rounding branch February 22, 2026 00:04
@Caball009
Copy link
Caball009 commented Feb 22, 2026

Did regular ceil still need the epsilon offset?

Yes - it guarantees calculations are always correct. Ceil does not eliminate floating point inaccuracies. 0.26 × 1,000 may work, but 4.01 × 1000 = 4,011.

I don't think those numbers support your point, either. ceil(4.01f * 1000) equals ceil((4.01f * 1000) - 0.0001f) in my tests.

CookieLandProjects pushed a commit to CookieLandProjects/CLP_AI that referenced this pull request Feb 28, 2026
@Caball009
Copy link

@Stubbjax

@Stubbjax
Copy link
Author
Stubbjax commented Mar 2, 2026

@Stubbjax

Well the point was that an epsilon is still needed. Perhaps we need a larger one for this scenario?

@Caball009
Copy link

I have yet to see an example where the epsilon would make a difference, though.

@Stubbjax
Copy link
Author
Stubbjax commented Mar 3, 2026

I have yet to see an example where the epsilon would make a difference, though.

I tested 999 multipliers between 0.01 - 9.99 on a build cost of 1000. It looks like WWMATH_EPSILON (0.0001) is too conservative, and still yields incorrect results for 4.01, 4.03, 4.05, 4.07, 4.09, 8.02, 8.06, 8.10, 8.14 and 8.18 (as unlikely as those multipliers are).

Increasing the epsilon to 0.001 resolves these inaccuracies and can be done in a follow-up change.

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

Labels

Bug Something is not working right, typically is user facing Gen Relates to Generals GLA Affects GLA faction Minor Severity: Minor < Major < Critical < Blocker NoRetail This fix or change is not applicable with Retail game compatibility ZH Relates to Zero Hour

Projects

None yet

Development

Successfully merging this pull request may close these issues.

GLA Bounty gives incorrect reward for some modded percentages

7 participants

0