Math in statement vs variables issue

This isn’t related to any specific course content, but I have created my own practice project to work on a little bit (a ring toss game).

I’ve run into a confusing thing and just want to know why it happens. I have a power meter to determine how much force is applied to the ring in order to toss it at the pegs. At the moment it’s just some text ranging from 1%-100% (14-1400 force), but I’m in the process of reworking it to confine it to 80%-100% (1120-1400 force) while still displaying as 1%-100%.

I used a few if statements to keep it in the 80-100 range, but found that it would get stuck at 80% when I used “if (currentPower < powerMax * 0.8f) {currentPower = powerMax * 0.8f;}”. I used “powerMax * 0.8f” instead of a variable because I figured I would only use it in that single method (other than to set the starting power) and didn’t want another power related variable.

After quite a while of scratching my head wondering why it wasn’t working I eventually made a variable and set it to 'powerMax * 0.8f" to make “if (currentPower < testingNumberThing) {currentPower = testingNumberThing}” and the code worked exactly as intended.

Now, to my limited knowledge these two codes were one and the same, so can someone educate me as to why they aren’t?


Here’s the broken code:

float powerMax;
float currentPower;

void Start()
    {
        powerMax = 1400f
        currentPower = powerMax * 0.8f;
    }

void ChangePower(float increment)

    {
        if (currentPower > powerMax)
            {currentPower = powerMax;}
        else if (currentPower < powerMax * 0.8f)
            {currentPower = powerMax * 0.8f;}
        else if (currentPower >= powerMax * 0.8f && currentPower <= powerMax)
            {currentPower += increment * Time.deltaTime;}
    }

here’s the working code:

float powerMax;
float testingNumberThing;
float currentPower;

void Start()
    {
        powerMax = 1400f
        testingNumberThing = powerMax * 0.8f;
        currentPower = testingNumberThing;
    }

void ChangePower(float increment)
    {
        if (currentPower > powerMax)
            {currentPower = powerMax;}
        else if (currentPower < testingNumberThing)
            {currentPower = testingNumberThing;}
        else if (currentPower >= testingNumberThing && currentPower <= powerMax)
            {currentPower += increment * Time.deltaTime;}
    }
1 Like

Hi!

What an interesting bug, it took me almost an hour to figure it out and actually I didn’t actually find out what is going on, I only have a slight suspicion.

I think you found a weird, weird, weird bug (it might be a floating number comparison bug, not 100% sure) and you should report it. I tested the following code:

    [SerializeField] float percentage = 0.8f;
    [SerializeField] float powerMax = 1400f;

    float currentPower;

    void Start()
    {
        currentPower = powerMax * percentage;
    }

    void ChangePower(float increment)
    {
        if (currentPower > powerMax)
        { currentPower = powerMax; }
        else if (currentPower < powerMax * percentage)
        { currentPower = powerMax * percentage; }
        else if (currentPower >= powerMax * percentage && currentPower <= powerMax)
        { currentPower += increment * Time.deltaTime; }
    }

    private void Update()
    {
        if (Input.GetKey(KeyCode.Space))
        {
            ChangePower(10);
        }

        if (Input.GetKeyUp(KeyCode.Space))
        {
            currentPower = 0;
        }
    }

I played with the values and I noticed that if you multiply by 0.8f it won’t work but it will work if you multiply by another number like 0.7f, the powerMax value is somewhat irrelevant but not really because sometimes, depending on its value, the code won’t work if you multiply by 0.79f or 0.81f, multiplying by 0.78f and below or 0.82 and above work just fine tho, which is hilarious.

My suggestion to fix this weird issue is to use Clamp instead of all those if statements, you can achieve the same result without the bug and in a single line of code.

Yeah I noticed the same thing too. At one point I decided to multiply by 0.81f just to keep it from getting stuck, but quickly decided that wasn’t really a proper solution. It’s not really a big issue anymore since I wasn’t going to keep the code anyway, it was sort of just a placeholder until I figured how to redo the power meter. I was just curious about what was happening. I’ve scrapped it all and turned it into a bar that oscillates the width so it takes a bit more skill to get a good toss. I don’t have a proper minimum power anymore like the old code did, but I’ll probably use this fancy new clamp technology you’ve just showed me. Thanks!

1 Like

When you assign a specific precalculated value to a float, that float retains that exact value. The moment you do any calculation on it, though, all bets are off. Due to the base-2 nature of the way a computer stores and uses numbers, the more precise a calculation is, the more memory and resource-intensive calculation is.

When we need more precision in math, we just add decibel numbers to the end. Want to know what 3 divided by 2 is? No problem. We add a decimal number and get a precise answer of 1.5. Computers can’t do it that. Each float only has so many bits to work with, and each bit is a 0 or a 1. Consequently, it’s calculations will only be so precise.

Generally these levels of precision are fast and accurate enough for our purposes. But if you’re going to ask a computer for an exact comparison (I e., are these two numbers equal, is this number higher) you want to also account for some kind of margin of error. I typically use 0.1f but the margin of error can be much smaller. So, if the difference between two floats is less than 0.1f, I write code that considers them equal.

Unity also has built in features called Epsilon and Approximately that help you account for this margin of error while being as close as possible.

For example, instead of
if (currentPower > blah blah blah
You could use:
If (currentPower + Mathf.Epsilon > blah blah blah
to account that currentPower may be slightly smaller than it should be by some infinitisemal amount.

2 Likes

Amazing explanation.

I have a question, Why it doesn’t show any difference when printing it? I tried showing all the decimal values and it’s the same all the time, Is the difference so tiny it can’t even be printed but it exists?

I’m not sure. I’d have to go home and do some tests.

I added more details to my post above, btw.

I had to check this outside Unity and… well… the plot thickens… This works perfectly fine outside Unity.

static void FloatComparison()
{
    if (currentPower > maxPower)
    { currentPower = maxPower; }
    else if (currentPower < maxPower * 0.8f)
    { currentPower = maxPower * 0.8f; }
    else if (currentPower >= maxPower * 0.8f && currentPower <= maxPower)
    { currentPower += 1f; }

    Console.WriteLine(currentPower);

    Console.Read();

    FloatComparison();
}

No errors at any point whatsoever, I tested it multiple times and it always worked, so… I’m just really confused. I know it’s a float comparison error but that means that it should sometimes work and sometimes it shouldn’t, Why in Unity it never works but compiling outside Unity works all the time? Why does it show the exact same values every time both in Unity and outside of it, yet throws very different results? This is going to give me nightmares :rofl:

Have your questions been answered, @Eastan_Trotchie?


See also:

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.

Privacy & Terms