[Possible bug] Issue when adding values in a simple function.

Hi,
I’ve written a little code like this:

	public float bar = 0f;
	private float value = 0.2475f;
	bool canIncrease = true;
	float maxManaSize = 0.99f;
	static public int manaBars =3;

	public void Start ()
	{
		value = value/manaBars;    //this gives 0.0825
	}
	IEnumerator IncreaseBoost ()
	{
		if (canIncrease)
		{
			if (bar < maxManaSize)
			{
				bar += value;
			}
		yield return new WaitForEndOfFrame ();
		}
	}

IncreaseBoost() function is called via button. So when I click button it adds values just fine until it reaches 0.9075. When I click one more time it gives me 0.9899999 instead of 0.99. Why?

(0.9075 + 0.0825 = 0.99)

This code should work for you too should you want to recreate it.

This is a result from floating point precision errors. The problem is that printing the values as readable text simplifies the actual representation which skews your expectations. I looked more closely at what happens at the binary level.

I wrote a script to analyse your information.

using System;
using System.Text;
using System.Linq;
using UnityEngine;

public class FloatingPointPrecision : MonoBehaviour
{
    void Start()
    {
        float value = 0.2475f;
        int manaBars = 3;

        float step = value / manaBars;
        float lhs = 0.9075f;
        float rhs = 0.0825f;
        float incr = IterateTo(lhs, step);
        float expectedResult = 0.99f;

        printFloat("lhs", lhs);
        printFloat("incr", incr);
        printFloat("rhs", rhs);
        printFloat("step", step);
        printFloat("lhs+rhs", lhs + rhs);
        printFloat("incr+step", incr + step);
        printFloat("Expected result", expectedResult);
    }

    static float IterateTo(float length, float step)
    {
        float incr = 0;
        for (; incr < length - step; incr += step) ;
        return incr;
    }

    static void printFloat(string label, float value)
    {
        print(label + "

"
+ ToHexString(value) + " : "
+ ToBinString(value) + " : "
+ value.ToString());
}

    static string ToHexString(float value)
    {
        byte[] raw = BitConverter.GetBytes(value);
        StringBuilder sb = new StringBuilder(raw.Length * 2);
        foreach (byte b in raw.Reverse())
        {
            sb.Append(b.ToString("x2"));
        }
        return sb.ToString();
    }

    static string ToBinString(float value)
    {
        byte[] raw = BitConverter.GetBytes(value);
        StringBuilder sb = new StringBuilder(raw.Length * 8);
        foreach (byte b in raw.Reverse())
        {
            sb.Append(Convert.ToString(b, 2).PadLeft(8, '0'));
        }
        return sb.ToString();
    }
}

I also include output, formatted slightly differently to make it easy to follow

lhs               3f6851ec : 00111111011010000101000111101100 : 0.9075    
incr              3f6851eb : 00111111011010000101000111101011 : 0.9075
rhs               3da8f5c3 : 00111101101010001111010111000011 : 0.0825
step              3da8f5c3 : 00111101101010001111010111000011 : 0.0825
lhs+rhs           3f7d70a4 : 00111111011111010111000010100100 : 0.99
incr+step         3f7d70a3 : 00111111011111010111000010100011 : 0.9899999
Expected result   3f7d70a4 : 00111111011111010111000010100100 : 0.99

You expect lhs (0.9075) + rhs (0.0825) to be 0.99. And it is correct. However, incrementing a variable by a float will cause rounding errors because a float cannot represent every possible fractional value. So, you expect incr to be the same as lhs, and, well, textually it is. But in actuality, there are small differences that can be seen when we look at the binary output.

lhs               3f6851ec : 00111111011010000101000111101100 : 0.9075    
incr              3f6851eb : 00111111011010000101000111101011 : 0.9075

Notice that **3f6851ec ** is the hex for 0.9075. **3f6851eb ** is also the hex for 0.9075. But they aren’t the same numbers (…ec, …eb)

The textual error is only visible when we add 0.0825 to lhs and incr.

lhs+rhs           3f7d70a4 : 00111111011111010111000010100100 : 0.99
incr+step         3f7d70a3 : 00111111011111010111000010100011 : 0.9899999

Note that rhs and step is the same value, so don’t get confused that we add different values to lhs and incr…

rhs               3da8f5c3 : 00111101101010001111010111000011 : 0.0825
step              3da8f5c3 : 00111101101010001111010111000011 : 0.0825

This isn’t a “bug” (well, it is in your code, I guess) but a behaviour of floating point binary arithmetic.