Skip to content

Fix edge case rounding issue with FMA instructions - #64

Open
bobjrsenior wants to merge 1 commit into
SpectralPack:mainfrom
bobjrsenior:main
Open

bobjrsenior wants to merge 1 commit into
SpectralPack:mainfrom
bobjrsenior:main

Conversation

@bobjrsenior

Copy link
Copy Markdown

There are a few seeds Immolate can output which don't actually match the game.

One example is for seed 124Q

$ ./Immolat124Q speedrun -s 124Q -n 1 -p 0 -g 1 -c 1
Immolate Beta v1.0.1f.1
Building program...
Starting searcher...
124Q (4)
Done in 0.002851s

It thinks the run survives 4 skips but Bull doesn't appear so it should be 0.

Insance.cl has the get_node_child function with

 inst->rngCache.nodes[node_id].rngState = roundDigits(fract(inst->rngCache.nodes[node_id].rngState*1.72431234+2.134453429141),13);

Mirrors Balatro's Lua from misc_functions.lua:

G.GAME.pseudorandom[key] = math.abs(tonumber(string.format("%.13f", (2.134453429141+G.GAME.pseudorandom[key]*1.72431234)%1)))

Balatro's math rounds G.GAME.pseudorandom[key]*1.72431234 to a double. It then adds 2.134453429141 and rounds it to 13 digits. For OpenCL, the GPU may use an FMA (Fused Multiple Add) instead of two separate operations which causes the value to only be rounded once instead of twice. In rare cases, the rounding difference can change the last digit of the value.

The FMA can be prevented by adding this pragma at the top of lib/Immolate.cl

#pragma OPENCL FP_CONTRACT OFF

With the change, Immolate sees that Bull doesn't exist and returns 0.

 ./Immolate -f speedrun -s 124Q -n 1 -p 0 -g 1 -c 1
Immolate Beta v1.0.1f.1
Building program...
Starting searcher...
Done in 0.002802s

Another example is 2X3T. Once again, Bull doesn't exist but Immolate thought it did.

Pre-Fix:

$ ./Immolat2X3T speedrun -s 2X3T -n 1 -p 0 -g 1 -c 1
Immolate Beta v1.0.1f.1
Building program...
Starting searcher...
Done in 0.004186s

Post Fix:

 ./Immolate -f speedrun -s 2X3T -n 1 -p 0 -g 1 -c 1
Immolate Beta v1.0.1f.1
Building program...
Starting searcher...
2X3T (4)
Done in 0.002891s

It can go the other way too but it's easier to verify examples where Immolate thought the Bull exists when it doesn't.

Note: I had the help of AI in figuring this out though I manually verified the seeds in game to see that it matches Immolate after adding the pragma. I also wrote the PR text myself.

In get_node_child of Instance.cl, there is a multiply then add
that can be combined into an FMA. That can cause rare issues
due to differing rounding, Balatro does mult->round->add0>round.
With FMA, it becomes mult->add->round instead. FMA is one less
rounding operation than Balatro does.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant