[1.1.2567 (64-bit) Non-Royalty] Batteries Self-Discharge at 3.66W Instead of 5W

Started by Homez, March 08, 2020, 11:47:26 AM

Previous topic - Next topic

Homez

Batteries self-discharge at 3.66W instead of 5W, but the issue is really much bigger. I believe this is a sort of rounding error due to the limitations of the float datatype that could impact the power system in other places besides just the battery building.

If you have an isolated battery fully charged with 600Wd of energy, it will self-discharge. The stated rate is 5W, meaning if you leave the battery alone, neither supplying energy nor consuming its energy, it will have 595Wd of energy after 1 day (60,000 ticks). This is not the case. In actuality, after 60,000 ticks, it will have about ‭596.34‬Wd of energy. This phenomenon can be trivially replicated by executing the following function anywhere in RimWorld:

private void SimulateBatterySelfDischarge()
        {
            float batteryStartingEnergyFloat = 600f;
            float energyFloat = batteryStartingEnergyFloat;

            decimal batteryStartingEnergyDecimal = 600m;
            decimal energyDecimal = batteryStartingEnergyDecimal;
            const decimal WattsToWattDaysPerTickDecimal = 1.66666669E-05m; // same value as CompPower.WattsToWattDaysPerTick, but as decimal

            int ticksInOneDay = 60000;

            for (var i = 0; i < ticksInOneDay; i++)
            {
                energyFloat -= 5f * CompPower.WattsToWattDaysPerTick;
                energyDecimal -= 5m * WattsToWattDaysPerTickDecimal;
            }

            Log.Message("After one simulated day (60,000 iterations):");
            Log.Message("Self-discharge when using float: " + (batteryStartingEnergyFloat - energyFloat));
            Log.Message("Self-discharge when using decimal: " + (batteryStartingEnergyDecimal - energyDecimal));

            Log.Message("Without the loop, float: " + (5f * CompPower.WattsToWattDaysPerTick * ticksInOneDay));
            Log.Message("Without the loop, decimal: " + (5m * WattsToWattDaysPerTickDecimal * ticksInOneDay));
        }


The energyFloat -= 5f * CompPower.WattsToWattDaysPerTick line is taken, with slight modification, from CompPowerBattery.CompTick(); it's the line responsible for the self-discharge.

After running that code, you'll notice that the decimal values are exactly as you'd expect: 5Wd have been discharged. batteryStartingEnergyDecimal - energyDecimal is 5. But the float value from batteryStartingEnergyFloat - energyFloat is 3.66.

Something about looping and performing that arithmetic on a float is causing a rounding error. Just for fun, I included Log.Message("Without the loop, float: " + (5f * CompPower.WattsToWattDaysPerTick * ticksInOneDay)) to verify it is, in fact, the loop causing the issue. If you do the daily discharge all at one time with 5f * CompPower.WattsToWattDaysPerTick * ticksInOneDay you get 5 right on the money.

If this isn't sufficient proof, I created a MapComponent class that will perform this experiment using real batteries and the real tick loop. See attached for the class. The results are exactly the same.

If you'd like to run it, you'll need a mod to drop the class into, then start the RimWorld testing map, pause immediately, go into god mode, spawn a battery, set it to full, and wait a few days. The log will print out, demonstrating this issue does exist in the game. I included the simple loop test in the file, too, for your amusement edification. Also included a picture of the real battery test in the attachments.

Also, this is my first post and I just started modding. Please be gentle.

zizard

Quote from: Homez on March 08, 2020, 11:47:26 AM
Also, this is my first post and I just started modding. Please be gentle.

This is probably the best first post that anyone will ever make about rimworld.

Homez

Quote from: zizard on March 08, 2020, 11:54:30 PM
Quote from: Homez on March 08, 2020, 11:47:26 AM
Also, this is my first post and I just started modding. Please be gentle.

This is probably the best first post that anyone will ever make about rimworld.

This is definitely the best first reply I've ever received on a forum. Really appreciate the friendly welcome.  :)

Tynan

Excellent report and the cause makes a lot of sense. It's just float precision problems - the small amount of energy that discharges per day gets divided by 60,000 into a tiny float, then subtracted from a large float. It must be inducing a bias in the calculations. Easy enough to fix by only discharging say, once per 200 ticks. Though not a super high priority bug, but nice catch!
Tynan Sylvester - @TynanSylvester - Tynan's Blog