Comparing a clamped float value with an integer (max value)...

Good day everyone!

I have code:

float a,b;
float lerp = a/b;
lerp = Mathf.Clamp(lerp, 0, 1);```

Which of these comparisons will work correctly?
a) if (lerp == 1)
OR
b) if (Mathf.Approximately(lerp, 1))
?

At the moment I'm using a) and it works OK, but I learned that floating numbers need to be compared using Mathf.Approximately.
In my case I'm comparing a float to an integer and I don't know how that works for c#. Maybe the integer is converted to float when compared?

I don’t recommend ever comparing float for equality, even if you feed constants in.

Mathf.Approximately is great, but if (lerp >= 1) is better. :slight_smile:

Or do your own notion of how close it has to be:

if (Mathf.Abs( lerp - 1) < 0.1f) ...

But remember if your terms move fast enough those will fail too.

That’s why I always like >=

ALSO, just as a note, *.Lerp() will actually clamp the third term already for you.

ALSO… here’s a pattern I really like:

float duration = 2.3f;

for (float t = 0; t < duration; t += Time.deltaTime)
{
  float fraction = t / duration;

/// do my lerp here using fraction as the third argument

   yield return null;
}

BUT! The above might never actually fire on 1.0 so you have to adjust after the loop exits.

This is a modified form that WILL perform the final 1.0f lerp, but it can be baffling:

float duration = 2.3f;

for (float t = 0; true; t += Time.deltaTime)
{
  float fraction = t / duration;

/// do my lerp here using fraction as the third argument

   yield return null;

   if (fraction >= 1) break;
}

Of course, if you’re just lerping stuff around, perhaps you should be using DOTween or LeanTween or some other tweener. No sense reinventing the wheel!

Thanks!

I have this code in my project, and it always stops at the right time. And I’m curious why I never got the error. Am I lucky, or is the probability too low?

float duration = 1.4f;
while (true)
{
    float lerp = time/duration;
    lerp = Mathf.Clamp(lerp, 0, 1);
    if (lerp == 1) break; // always works;
    time += Time.deltaTime;
    yield return null;
}

In theory line 5 should clamp it at the constant 1

This allows your line 6 to succeed, as it compares to a constant.

However, should you in the future change that value like to 1.1 … it might stop working.

Now you’re very unlikely to… but stuff like that happens all the time.

Why do you think software (in general) in this world is so @$$##@@# ridiculously buggy? :slight_smile:

Here’s some more reading, including a cool visualizer at the bottom.

Floating (float) point imprecision:

Never test floating point (float) quantities for equality / inequality. Here’s why:

https://starmanta.gitbooks.io/unitytipsredux/content/floating-point.html

https://discussions.unity.com/t/851400/4

https://discussions.unity.com/t/843503/4

“Think of [floating point] as JPEG of numbers.” - orionsyndrome on the Unity3D Forums

Literal float / double issues:

https://discussions.unity.com/t/836836/2

And thanks to halley for this handy little visual floating point converter:

https://www.h-schmidt.net/FloatConverter/IEEE754.html

Just to provide a little deeper explanation on this…

“Floating point errors” are a side-effect of the fact that fractional numbers (like floats, halves, doubles, etc.) are not “infinite” in precision - they “round” to the “most precise” decimal point they have available. A short basic explanation of this is “the bigger the number on the left side of the decimal point, the less precision you have on the right side of the decimal point”.

This especially comes up when you compare two fractional numbers that are the result of a multiply or, especially, a divide. If you do the same math for two numbers, they will probably end up equal, but different processors will actually round differently, or have different levels of precision - so you may end up with different numbers even with the same math between them.

Now, specifically to answer your question:
If you are comparing a float to an int, it should be relatively safe to compare your int to Mathf.CeilToInt(myFloat) or Mathf.FloorToInt(myInt) - though you may still run into trouble when your float could be 0.9999999 instead of 1.0000000 (approximately) on different devices.
Similarly, Mathf.RoundToInt is relatively safe, but would have issues when the float might be 0.4999999 versus 0.5000000.

So as Kurt-Dekker pointed out - it’s never 100% safe to directly compare floats to anything for equality, but you should be pretty safe comparing for “approximate equality” - which means using Mathf.Approximately is the way to go if you are worried about having consistent results across devices (there may still be issues even there, though, so be careful).

Also, you should understand that you are not actually comparing a float to an int. The comparison operators only work on two values of the same type. So, even if you write an int literal, or one of the variables is a float, the comparison is always taking place between a float and a float, or an int and an int. I’m not checking your code specifically in my IDE right now, but if you hover over the comparison operator in Visual Studio with your mouse it will tell you which types are being compared. The types you present are implicitly converted to those types before the comparison. So, you’re not really comparing a float to an int. Make sure you understand that and inspect your comparison operator to know which values are being converted to which types for comparison.

Good point, and yes, usually if you are doing any operations between integers and decimal numbers, it’ll prefer the greatest precision from among them - so int will be converted to float when comparing against a float.

There are also some weird cases where things get converted to doubles, when you’re not actually using any doubles, but that’s a whole other can of worms…