# Why Doesn't This Code Work?

Maybe I’m just sleep-deprived, but I can’t see a single reason why this code doesn’t work. I came across the issue while using Scriptable Objects in one of my Unity projects, but was able to replicate it in a short C# program on .NET Fiddle.

The output is `Does 5 equal 5? False`

Can someone explain this to me?

``````using System;

public class Program
{
public static void Main()
{
Test t = new Test();
float five = 5;
Console.Write("Does " + five + " equal " + (1 / t.num) + "? " + (five == (1 / t.num)));
}
}

public class Test
{
private float _num;

public float num
{
get
{
return _num;
}
}

public Test()
{
_num = 0.2f;
}
}
``````

The issue you are seeing is due to how floating point numbers are handled in c# (and many other languages). You need to compare them with another function that allows for some tolerance that you provide. Look at this article, it describes the issue in detail.

I’ve definitely heard of the difficulties that can arise from floating point precision. Now that I know the issue is float-related (I assumed it was something property-related before), I simplified the code to this, which still gives the same output:

``````using System;

public class Program
{
public static void Main()
{
float five = 5;
float pointTwo = 0.2f;
Console.Write("Does " + five + " equal " + (1 / pointTwo) + "? " + (five == (1 / pointTwo)));
}
}
``````

I did read the article you linked, but one particular aspect of this situation still doesn’t make sense to me. If I assign the expression “1 / pointTwo” to a float variable first, I get the “correct” output.

``````using System;

public class Program
{
public static void Main()
{
float five = 5;
float pointTwo = 0.2f;
float otherFive = 1 / pointTwo;
Console.Write("Does " + five + " equal " + otherFive + "? " + (five == otherFive));
}
}
``````

If the value of “1 / pointTwo” wasn’t quite five, but slightly more or less than five by some extremely small margin, why would that change just be assigning that value to a variable? Again, the code now reports that five and otherFive are EXACTLY equal.

This will explain it all.

In an IEEE single-floating point number you can represent certain numbers perfectly and unambiguously.

For instance, 0.5f is precisely represented as 0x3f000000

However, 0.1f CANNOT be precisely represented. One possible approximate representation is 0x3DCCCCCD

The analogy is that you cannot show 1/3rd precisely as a decimal.

0.33 is correct to 2 decimal places
0.3333 is correct to 4 places
etc.

But you can never exactly represent 1/3rd as a decimal numeral. You just get closer.

Three times 0.33 (one possible representation of 1/3rd) is 0.99… and last I checked, 0.99 does not equal 1.0… and yet “three times one third” should be 1.0.

Since computers store everything internally as binary (that’s how digital computers work!), you can NEVER, no matter how big a binary number you make, precisely represent 0.1f

And in your example 0.2f is simply 0.1f times 2, so not representable precisely.

But you CAN use other ways to represent it, such as with a C# Decimal, but that is nowhere near as efficient computationally, and is rarely ever used. I don’t even know if Unity’s .NET supports Decimal.

And a broken clock is right twice a day. The fact that you occasionally get the correct answer doesn’t mean that there are any circumstances you can expect floating point imprecision to not affect the result. The advice is the same: Never use == to compare any floats on which any math has been performed. Use Mathf.Approximately, or check against a distance or range.

Wow, this whole discussion really makes me a lot more wary of using floats at all…which is a weird feeling, because we kind of need to use floats all the time!

I found an answer on Stack Overflow saying that it’s “very, very hard, if not outright impossible,” to write a function that will correctly compare ANY float to ANY other float (.net - Floating point comparison functions for C# - Stack Overflow). Any input on this? If true, I even have to be skeptical about Mathf.Approximately, and about any tolerance range I come up with on my own.

It feels like a “speed bump” in my workflow that I’ll constantly have to deal with from now on. A necessary one, probably, and there’s no point complaining about something necessary, but still. Can anyone tell me I’m overreacting? Do you ever get held up like this in your projects, just because Mathf.Approximately (or a similar function) randomly had a slightly-too-small tolerance?

Yes, but that’s okay. It feels a lot worse than it actually is.

In practice it is never has to be a limitation. A tiny bit of extra code and you’re good.

In practice, floats are awesome, just not for comparing with equality.

That’s the tl;dr from all this: just NEVER think “is this equal to that?” with floats / doubles

With integers and strings you can do equality comparisons all day long, just not `float` / `double`.

And don’t use `double` unless you truly understand why you need to, and when you are in Unity, you also understand where double gets off the train and `float` takes over inside Unity.

No because I don’t use it. If I need to know exactly when a float is crossing a boundary I keep the previous and compare it to current.

``````private float prevValue;
``````
``````void Update()
{
if ((currValue >= TriggerValue) && (prevValue < TriggerValue))
{
BoomTrigger();
}
prevValue = currValue; // ninja-edited to stick this in; I forgot it on the first post
}
``````

Great advice. This has been a really valuable lesson–thanks everyone!

1 Like

I can always recommend this Computerphile video on Floating Point Numbers. It should give you the basic idea how floating point numbers work in general and where this problem actually comes from.

Note: by “compare” they mean compare for equality not comparing in general. In almost all cases when you want to compare two floats you want to check if one is larger or smaller than the other.

2 Likes

Hey, I love Computerphile’s stuff! I have watched that video and found it very interesting.

Yeah, come to think of it, I usually only compare floats with > or <, such as when a timer based on Time.deltaTime elapses. It took a very specific situation to reveal the gap in my understanding!