Can someone point towards (or show) some good general floating point comparison functions in C# for comparing floating point values? I want to implement functions for IsEqual
, IsGreater
an IsLess
. I also only really care about doubles not floats.

- 202,030
- 26
- 332
- 449

- 4,350
- 6
- 30
- 31
-
1What is your question, exactly? – NullUserException Oct 06 '10 at 16:16
-
Can someone point towards (or show) some good general floating point comparison functions in C# for comparing floating point values? The problem is a lot of people have shown partial answers. I'm looking for something more complete. – Kevin Gale Oct 06 '10 at 16:18
-
2This is dangerous, it pretends there's a meaningful result when the numbers get meaningless. Pay attention to Philip's post. – Hans Passant Oct 06 '10 at 18:05
-
1@Hans Passant - I don't see how Philip's post helps. I'm not claiming this function I found is good or correct I'm looking for help on that front. – Kevin Gale Oct 06 '10 at 18:19
-
@NullUserException - Thanks for adding the c# tag. Don't know how I missed that. – Kevin Gale Oct 06 '10 at 18:21
-
1When asking about floating point equality comparisons previously on SO, [I was given this advice](http://stackoverflow.com/questions/3281237/is-the-use-of-machine-epsilon-appropriate-for-floating-point-equality-tests/3281380#3281380): _"The question is, do you really want/need to do equality tests on floating point values? Maybe you should redesign your algorithms."_ That is, not having to do such a comparison in the first place relieves you from worrying about how to get it right. – stakx - no longer contributing Oct 08 '10 at 07:32
-
@stakx I came to this through Google and had the same question. I want to compare latitude and longitude coordinates. These are in the form of positive and negative decimal numbers - except you can't treat these as `decimal` or it truncates the decimal point if you store it in a decimal field in the database (was weird to me, in and of itself). So I'm stuck with doubles & floats like Kevin was. – vapcguy Sep 11 '18 at 18:57
-
Potential duplicate - https://stackoverflow.com/questions/30727449/comparing-two-decimals – vapcguy Sep 19 '18 at 14:00
15 Answers
Writing a useful general-purpose floating point IsEqual
is very, very hard, if not outright impossible. Your current code will fail badly for a==0
. How the method should behave for such cases is really a matter of definition, and arguably the code would best be tailored for the specific domain use case.
For this kind of thing, you really, really need a good test suite. That's how I did it for The Floating-Point Guide, this is what I came up with in the end (Java code, should be easy enough to translate):
public static boolean nearlyEqual(float a, float b, float epsilon) {
final float absA = Math.abs(a);
final float absB = Math.abs(b);
final float diff = Math.abs(a - b);
if (a == b) { // shortcut, handles infinities
return true;
} else if (a == 0 || b == 0 || absA + absB < Float.MIN_NORMAL) {
// a or b is zero or both are extremely close to it
// relative error is less meaningful here
return diff < (epsilon * Float.MIN_NORMAL);
} else { // use relative error
return diff / (absA + absB) < epsilon;
}
}
You can also find the test suite on the site.
Appendix: Same code in c# for doubles (as asked in questions)
public static bool NearlyEqual(double a, double b, double epsilon)
{
const double MinNormal = 2.2250738585072014E-308d;
double absA = Math.Abs(a);
double absB = Math.Abs(b);
double diff = Math.Abs(a - b);
if (a.Equals(b))
{ // shortcut, handles infinities
return true;
}
else if (a == 0 || b == 0 || absA + absB < MinNormal)
{
// a or b is zero or both are extremely close to it
// relative error is less meaningful here
return diff < (epsilon * MinNormal);
}
else
{ // use relative error
return diff / (absA + absB) < epsilon;
}
}

- 342,105
- 78
- 482
- 720
-
Thanks. This isn't my code actually just something I found and I could see problems with it and I hoped that would spur someone to answer my question. Do you have any input or examples for IsLess and IsGreater functions? – Kevin Gale Oct 06 '10 at 18:41
-
@Kevin: no, but it's not all that difficult to come up with test cases yourself. It's an almost perfect example for unit testing and a test-first approach: any change that makes one case work has a very high chance to break another. Without a test suite, it's nearly impossible to get all cases to work at the same time. – Michael Borgwardt Oct 06 '10 at 18:59
-
@MichaelBorgwardt Fantastic guide! For your unit tests I see that you are using the default `epsilon` of `0.000001f`. What would you recommend for performing the same unit tests with double precision? – Lea Hayes Feb 13 '13 at 03:53
-
Also, would you mind explaining the test case `assertTrue(nearlyEqual(1e10f * Float.MIN_VALUE, -1e10f * Float.MIN_VALUE));` because this one I cannot grasp for some reason... – Lea Hayes Feb 13 '13 at 04:02
-
This always seems to fail for me when implemented in C#... `diff / (absA + absB)` is `2` and `epsilon` is `0.000001f`. – Lea Hayes Feb 13 '13 at 06:19
-
@MichaelBorgwardt Sorry, the `diff / (absA + absB)` is 1 for that test (not 2 like I said before). In fact when I give any test like `a = -0.0001` and `b = +0.0001` the differences is always the same as the summation of the absolute values and thus resulting in `1` which fails the test because 1 > epsilon. Here is my C# implementation: http://pastebin.com/ZGhX1xBe. For the unit tests I am using `float.Epsilon` instead of `Float.MIN_VALUE` because these seem the most similar based upon the docs. – Lea Hayes Feb 13 '13 at 17:21
-
@Lea Hayes: The default epsilon has no special reason, just choose whatever relative error you think is acceptable. It can probably be smaller if you use doubles and the only source of errors is rounding. As for the test that fails: it's just two pretty damn small numbers on opposite sides of zero. In Java it works because the `if (a * b == 0)` condition is true: the multiplication underflows. Apparently .net handles this differently - perhaps it implicitly does the calculation in double precision? I guess this could be considered a bug in the method. – Michael Borgwardt Feb 13 '13 at 19:58
-
@Lea Hayes: But if you replace the condition with if `(a == 0 || b == 0)`, you run into weird behavior too: `Float.MIN_VALUE` and `-Float.MIN_VALUE` are not considered equal, but both are considered equal to 0. – Michael Borgwardt Feb 13 '13 at 19:59
-
1`+float.Epsilon` and `-float.Epsilon` are not considered equal because they are the smallest representable floating point values that are not zero. Which is obviously different from the behaviour you describe of `Float.MIN_VALUE`. Do you think that this functionality is feasible in C#? Another person on this page is experiencing the same issue: http://stackoverflow.com/questions/3874627/floating-point-comparison-functions-for-c-sharp/3875619#answer-6678089 – Lea Hayes Feb 13 '13 at 21:32
-
One thing which seemed to improve matters (though this was a hacky guess) was to replace the condition with `a * b == 0 || diff == (absA + absB)` this failed just 6 of the tests whereas the current function fails 11 (if I recall) of the tests. – Lea Hayes Feb 13 '13 at 22:16
-
@Lea Hayes: actually float.Epsilon is exactly the same thing as Float.MIN_VALUE in Java. And shouldn't the two smallest non-zero values, which are closer together than any other representable values (except zero) be considered equal in any case? – Michael Borgwardt Feb 13 '13 at 22:40
-
1`float.Epsilon == -float.Epsilon` is `false`. After additional experimentation I am finding that the following behaves the best so far (failing 5 tests): http://pastebin.com/xC8NddSA – Lea Hayes Feb 13 '13 at 23:09
-
@Lea Hayes: I cannot understand why float.Epsilon != -float.Epsilon has any impact on this discussion. – Michael Borgwardt Feb 13 '13 at 23:56
-
1?? those are the two smallest non-zero values which was what I thought you were referring to. I have included information about the failing tests in my previous paste (which you probably saw) – Lea Hayes Feb 14 '13 at 00:02
-
1Looking at these 5 failed cases it seems that they are in fact valid (for the C# implementation at least) because the difference between the operands is significantly smaller than the epsilon argument. For example surely the following should test true. `AlmostEqual(1.401298E-20f, -1.401298E-20f, 1E-12f)` I would expect it to also pass with `1E-19f` but fail with `1E-20f`. Am I right? Without the additional check [1] that I proposed in the paste I would otherwise still be getting other failed tests. – Lea Hayes Feb 14 '13 at 01:43
-
let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/24482/discussion-between-lea-hayes-and-michael-borgwardt) – Lea Hayes Feb 14 '13 at 01:44
-
@Michael Borgwardt: Revisting this and I have one question. You do the three abs calcs up front but in fact none of them may be used. Maybe I'm micro optimizing but wouldn't it make sense to wait until they are needed. – Kevin Gale Mar 14 '13 at 18:32
-
1@Kevin Gale: Yes, it's a micro-optimization, and the kind compilers can do pretty well, so best left to them unless your profiler tells you otherwise. – Michael Borgwardt Mar 14 '13 at 19:11
-
@MichaelBorgwardt thanks for this solution, i am looking for the same but one thing I cannot figured out is what is value that we need to pass for this parameter "float epsilon", would you pls suggest any ideas .... – Glory Raj Jul 22 '13 at 05:54
-
1Just a little tidbit. In C#, if both values are `Double.NaN`, `a == b` will evaluate to false. `.Equals()` should be used instead of `==` for the first case. – jchitel Sep 23 '14 at 19:52
-
@MichaelBorgwardt Hm. It seems that `NaN != NaN` is built into the IEEE floating point specification. Where I work, we use NaN as a placeholder when there is no available value, which happens often, so to us, NaN should be equal to NaN. I would imagine that Microsoft added `NaN.Equals(NaN)` to allow some people to use NaN as my team does. – jchitel Sep 23 '14 at 22:09
-
@jchitel this kind of thing is why I wrote "the code would best be tailored for the specific domain use case" – Michael Borgwardt Sep 24 '14 at 07:12
-
3For c#, [`Double.MinValue`](https://msdn.microsoft.com/en-us/library/system.double.minvalue%28v=vs.110%29.aspx) does not do what is required. It returns the negative number with the largest possible absolute value, `-1.7976931348623157E+308`. [`double.Epsiilon`](https://msdn.microsoft.com/en-us/library/system.double.epsilon%28v=vs.110%29.aspx) looks to correspond to [`Float.Min_Value`](http://docs.oracle.com/javase/7/docs/api/constant-values.html#java.lang.Float.MIN_VALUE); there is no equivalent to `Min_Normal`. Maybe you want something like `(1e7)*double.Epsilon`? – dbc Jul 22 '15 at 18:21
-
Should have used [`Double.Min_Value`](http://docs.oracle.com/javase/7/docs/api/constant-values.html#java.lang.Double.MIN_VALUE) not `Float`, so I reckon the equivalent c# expression would be `const double MinNormal = (1L << 52) * double.Epsilon;`. – dbc Jul 22 '15 at 19:11
-
Note, the C# implementation requires either `using System;` at the top or prefacing all `Math` references and the one `Double` reference with a `System.` – Greg Hilston Jul 03 '18 at 01:50
-
1Found no issues with the C# version in that "Appendix" when I used it, except that I was lost in these comments with how `epsilon` worked until I started tinkering with the code on my own & actually making it run through its paces (which I recommend to anyone to do first). There was a similar question to where I reference this answer, and explain what I found regarding `epsilon` and how it's used at: https://stackoverflow.com/questions/30727449/comparing-two-decimals/52410610#52410610 – vapcguy Sep 19 '18 at 16:46
-
2I think this function does not behave as intended when both supplied floats differ in signs. `NearlyEqual(1000.0, 2999.0, 1)` has a mix of different and similar behaviours than `NearlyEqual(1000.0, -999.0, 1)`. The absolute difference is treated as if the latter were `NearlyEqual(1000.0, 2999.0, 1)` but `(absA + absB)` is treated as if the latter were `NearlyEqual(1000.0, 1999.0, 1)`. – Benoît Dubreuil Aug 24 '20 at 22:58
-
What I meant is that `diff / (absA + absB)` will always result in `1.0` approximately if `a` and `b` have different signs. – Benoît Dubreuil Aug 25 '20 at 13:48
-
1The double code doesn't seem to work.... `0 != 0.000046` here, even with a provided epsilon of `0.1` – Douglas Gaskell Mar 30 '22 at 05:09
From Bruce Dawson's paper on comparing floats, you can also compare floats as integers. Closeness is determined by least significant bits.
public static bool AlmostEqual2sComplement( float a, float b, int maxDeltaBits )
{
int aInt = BitConverter.ToInt32( BitConverter.GetBytes( a ), 0 );
if ( aInt < 0 )
aInt = Int32.MinValue - aInt; // Int32.MinValue = 0x80000000
int bInt = BitConverter.ToInt32( BitConverter.GetBytes( b ), 0 );
if ( bInt < 0 )
bInt = Int32.MinValue - bInt;
int intDiff = Math.Abs( aInt - bInt );
return intDiff <= ( 1 << maxDeltaBits );
}
EDIT: BitConverter is relatively slow. If you're willing to use unsafe code, then here is a very fast version:
public static unsafe int FloatToInt32Bits( float f )
{
return *( (int*)&f );
}
public static bool AlmostEqual2sComplement( float a, float b, int maxDeltaBits )
{
int aInt = FloatToInt32Bits( a );
if ( aInt < 0 )
aInt = Int32.MinValue - aInt;
int bInt = FloatToInt32Bits( b );
if ( bInt < 0 )
bInt = Int32.MinValue - bInt;
int intDiff = Math.Abs( aInt - bInt );
return intDiff <= ( 1 << maxDeltaBits );
}

- 441
- 3
- 7
-
1Interesting. I come across a few references that seem to say this may be the best way to do it (comparing as an integer type). Michael Borgwardt above also links to Dawson's paper. I wonder if the bit converting is very expensive? – Kevin Gale Oct 07 '10 at 14:30
-
BitConverter is slow. I've added a much faster version, but it uses unsafe code. – Andrew Wang Oct 08 '10 at 07:24
-
Thanks I'll consider it and it will be useful to others who find this question. – Kevin Gale Oct 08 '10 at 14:10
-
2Is there a way to convert an absolute error from float into `maxDeltaBits` so that this function works similar (but more accurate of course) to `abs(a - b) < delta`? I like the idea of this approach, but I would prefer a function where I can specify a maximum absolute error. – Lea Hayes Feb 14 '13 at 04:08
-
Also BitConverter method runs under unsafe context, but it contains validation check, so it'll slower than your direct code but it is "computer time". – SlaneR Jan 24 '17 at 06:41
-
Beware of this logic because some results might be unexpected. For example when maxDeltaBits is set to 1 this logic considers 0.00000000000000000000000000000000000000000001 and 0 different but 1.0000001 and 1 same. – RM. Sep 03 '20 at 07:12
Be careful with some answers...
UPDATE 2019-0829, I also included Microsoft decompiled code which should be far better than mine.
1 - You could easily represent any number with 15 significatives digits in memory with a double. See Wikipedia.
2 - The problem come from calculation of floating numbers where you could loose some precision. I mean that a number like .1 could become something like .1000000000000001 ==> after calculation. When you do some calculation, results could be truncated in order to be represented in a double. That truncation brings the error you could get.
3 - To prevent the problem when comparing double values, people introduce an error margin often called epsilon. If 2 floating numbers only have a contextual epsilon as difference, then they are considered equals. double.Epsilon is the smallest number between a double value and its neigbor (next or previous) value.
4 - The difference betwen 2 double values could be more than double.epsilon. The difference between the real double value and the one computed depends on how many calculation you have done and which ones. Many peoples think that it is always double.Epsilon but they are really wrong. To have a great answer please see: Hans Passant answer. The epsilon is based on your context where it depends on the biggest number you reach during your calculation and on the number of calculation you are doing (truncation error accumulate).
5 - This is the code that I use. Be careful that I use my epsilon only for few calculations. Otherwise I multiply my epsilon by 10 or 100.
6 - As noted by SvenL, it is possible that my epsilon is not big enough. I suggest to read SvenL comment. Also, perhaps "decimal" could do the job for your case?
Microsoft decompiled code:
// Decompiled with JetBrains decompiler
// Type: MS.Internal.DoubleUtil
// Assembly: WindowsBase, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
// MVID: 33C590FB-77D1-4FFD-B11B-3D104CA038E5
// Assembly location: C:\Windows\Microsoft.NET\assembly\GAC_MSIL\WindowsBase\v4.0_4.0.0.0__31bf3856ad364e35\WindowsBase.dll
using MS.Internal.WindowsBase;
using System;
using System.Runtime.InteropServices;
using System.Windows;
namespace MS.Internal
{
[FriendAccessAllowed]
internal static class DoubleUtil
{
internal const double DBL_EPSILON = 2.22044604925031E-16;
internal const float FLT_MIN = 1.175494E-38f;
public static bool AreClose(double value1, double value2)
{
if (value1 == value2)
return true;
double num1 = (Math.Abs(value1) + Math.Abs(value2) + 10.0) * 2.22044604925031E-16;
double num2 = value1 - value2;
if (-num1 < num2)
return num1 > num2;
return false;
}
public static bool LessThan(double value1, double value2)
{
if (value1 < value2)
return !DoubleUtil.AreClose(value1, value2);
return false;
}
public static bool GreaterThan(double value1, double value2)
{
if (value1 > value2)
return !DoubleUtil.AreClose(value1, value2);
return false;
}
public static bool LessThanOrClose(double value1, double value2)
{
if (value1 >= value2)
return DoubleUtil.AreClose(value1, value2);
return true;
}
public static bool GreaterThanOrClose(double value1, double value2)
{
if (value1 <= value2)
return DoubleUtil.AreClose(value1, value2);
return true;
}
public static bool IsOne(double value)
{
return Math.Abs(value - 1.0) < 2.22044604925031E-15;
}
public static bool IsZero(double value)
{
return Math.Abs(value) < 2.22044604925031E-15;
}
public static bool AreClose(Point point1, Point point2)
{
if (DoubleUtil.AreClose(point1.X, point2.X))
return DoubleUtil.AreClose(point1.Y, point2.Y);
return false;
}
public static bool AreClose(Size size1, Size size2)
{
if (DoubleUtil.AreClose(size1.Width, size2.Width))
return DoubleUtil.AreClose(size1.Height, size2.Height);
return false;
}
public static bool AreClose(Vector vector1, Vector vector2)
{
if (DoubleUtil.AreClose(vector1.X, vector2.X))
return DoubleUtil.AreClose(vector1.Y, vector2.Y);
return false;
}
public static bool AreClose(Rect rect1, Rect rect2)
{
if (rect1.IsEmpty)
return rect2.IsEmpty;
if (!rect2.IsEmpty && DoubleUtil.AreClose(rect1.X, rect2.X) && (DoubleUtil.AreClose(rect1.Y, rect2.Y) && DoubleUtil.AreClose(rect1.Height, rect2.Height)))
return DoubleUtil.AreClose(rect1.Width, rect2.Width);
return false;
}
public static bool IsBetweenZeroAndOne(double val)
{
if (DoubleUtil.GreaterThanOrClose(val, 0.0))
return DoubleUtil.LessThanOrClose(val, 1.0);
return false;
}
public static int DoubleToInt(double val)
{
if (0.0 >= val)
return (int) (val - 0.5);
return (int) (val + 0.5);
}
public static bool RectHasNaN(Rect r)
{
return DoubleUtil.IsNaN(r.X) || DoubleUtil.IsNaN(r.Y) || (DoubleUtil.IsNaN(r.Height) || DoubleUtil.IsNaN(r.Width));
}
public static bool IsNaN(double value)
{
DoubleUtil.NanUnion nanUnion = new DoubleUtil.NanUnion();
nanUnion.DoubleValue = value;
ulong num1 = nanUnion.UintValue & 18442240474082181120UL;
ulong num2 = nanUnion.UintValue & 4503599627370495UL;
if (num1 == 9218868437227405312UL || num1 == 18442240474082181120UL)
return num2 > 0UL;
return false;
}
[StructLayout(LayoutKind.Explicit)]
private struct NanUnion
{
[FieldOffset(0)]
internal double DoubleValue;
[FieldOffset(0)]
internal ulong UintValue;
}
}
}
My code:
public static class DoubleExtension
{
// ******************************************************************
// Base on Hans Passant Answer on:
// https://stackoverflow.com/questions/2411392/double-epsilon-for-equality-greater-than-less-than-less-than-or-equal-to-gre
/// <summary>
/// Compare two double taking in account the double precision potential error.
/// Take care: truncation errors accumulate on calculation. More you do, more you should increase the epsilon.
public static bool AboutEquals(this double value1, double value2)
{
double epsilon = Math.Max(Math.Abs(value1), Math.Abs(value2)) * 1E-15;
return Math.Abs(value1 - value2) <= epsilon;
}
// ******************************************************************
// Base on Hans Passant Answer on:
// https://stackoverflow.com/questions/2411392/double-epsilon-for-equality-greater-than-less-than-less-than-or-equal-to-gre
/// <summary>
/// Compare two double taking in account the double precision potential error.
/// Take care: truncation errors accumulate on calculation. More you do, more you should increase the epsilon.
/// You get really better performance when you can determine the contextual epsilon first.
/// </summary>
/// <param name="value1"></param>
/// <param name="value2"></param>
/// <param name="precalculatedContextualEpsilon"></param>
/// <returns></returns>
public static bool AboutEquals(this double value1, double value2, double precalculatedContextualEpsilon)
{
return Math.Abs(value1 - value2) <= precalculatedContextualEpsilon;
}
// ******************************************************************
public static double GetContextualEpsilon(this double biggestPossibleContextualValue)
{
return biggestPossibleContextualValue * 1E-15;
}
// ******************************************************************
/// <summary>
/// Mathlab equivalent
/// </summary>
/// <param name="dividend"></param>
/// <param name="divisor"></param>
/// <returns></returns>
public static double Mod(this double dividend, double divisor)
{
return dividend - System.Math.Floor(dividend / divisor) * divisor;
}
// ******************************************************************
}

- 10,996
- 11
- 84
- 119
-
1You and Hans make a lot of sense and explain it quite nicely. After some tests I have to come to the conclusion that `1e-15` is even on the big side. When adding n times 1/n to a sum-value even a epsilon of 1e-15 is 'failing' in 999,282 out of n from 1 till 1e6. [Picking your epsilon](https://stackoverflow.com/a/2411773/578295) by Eric Lippert seems be be another way of looking at the problem. – SvenL Oct 05 '17 at 05:17
-
1@SvenL, thanks, I added a comment in my answer about it. I red Eric Lippert answer and will keep that in mind. – Eric Ouellet Oct 05 '17 at 13:24
Further to Andrew Wang's answer: if the BitConverter method is too slow but you cannot use unsafe code in your project, this struct is ~6x quicker than BitConverter:
[StructLayout(LayoutKind.Explicit)]
public struct FloatToIntSafeBitConverter
{
public static int Convert(float value)
{
return new FloatToIntSafeBitConverter(value).IntValue;
}
public FloatToIntSafeBitConverter(float floatValue): this()
{
FloatValue = floatValue;
}
[FieldOffset(0)]
public readonly int IntValue;
[FieldOffset(0)]
public readonly float FloatValue;
}
(Incidentally, I tried using the accepted solution but it (well my conversion at least) failed some of the unit tests also mentioned in the answer. e.g. assertTrue(nearlyEqual(Float.MIN_VALUE, -Float.MIN_VALUE));
)

- 1,391
- 9
- 24
Continuing from the answers provided by Michael and testing, an important thing to keep in mind when translating the original Java code to C# is that Java and C# define their constants differently. C#, for instance, lacks Java's MIN_NORMAL, and the definitions for MinValue differ greatly.
Java defines MIN_VALUE to be the smallest possible positive value, while C# defines it as the smallest possible representable value overall. The equivalent value in C# is Epsilon.
The lack of MIN_NORMAL is problematic for direct translation of the original algorithm - without it, things start to break down for small values near zero. Java's MIN_NORMAL follows the IEEE specification of the smallest possible number without having the leading bit of the significand as zero, and with that in mind, we can define our own normals for both singles and doubles (which dbc mentioned in the comments to the original answer).
The following C# code for singles passes all of the tests given on The Floating Point Guide, and the double edition passes all of the tests with minor modifications in the test cases to account for the increased precision.
public static bool ApproximatelyEqualEpsilon(float a, float b, float epsilon)
{
const float floatNormal = (1 << 23) * float.Epsilon;
float absA = Math.Abs(a);
float absB = Math.Abs(b);
float diff = Math.Abs(a - b);
if (a == b)
{
// Shortcut, handles infinities
return true;
}
if (a == 0.0f || b == 0.0f || diff < floatNormal)
{
// a or b is zero, or both are extremely close to it.
// relative error is less meaningful here
return diff < (epsilon * floatNormal);
}
// use relative error
return diff / Math.Min((absA + absB), float.MaxValue) < epsilon;
}
The version for doubles is identical save for type changes and that the normal is defined like this instead.
const double doubleNormal = (1L << 52) * double.Epsilon;

- 89
- 1
- 1
-
2If the computed normals are supposed to be IEEE-compliant substitutes for double.Epsilon and float.Epsilon (from which the computed normals are different), why are they multiplied with the given epsilon when compared with the diff, when the diff is smaller than the normal? Won't the epsilon parameter then simply work as a multiplier, increasingly diminishing the precision, or if below 1, increase the precision beyond the computed normal? I just don't see how the same epsilon value can be used in both return statements, with the same "meaning", if you will. Then again, I'm no math genius. – Ultroman the Tacoman Jun 12 '17 at 22:24
For people coming here for UNITY specific
There is Mathf.Approximately
so writing
if(Mathf.Approximately(a, b))
basically equals writing
if(Mathf.Abs(a - b) <= Mathf.Epsilon)
where Mathf.Epsilon
The smallest value that a float can have different from zero.
under the hood this further means (from the source code)
// A tiny floating point value (RO). public static readonly float Epsilon = UnityEngineInternal.MathfInternal.IsFlushToZeroEnabled ? UnityEngineInternal.MathfInternal.FloatMinNormal : UnityEngineInternal.MathfInternal.FloatMinDenormal; ... [Unity.IL2CPP.CompilerServices.Il2CppEagerStaticClassConstruction] public partial struct MathfInternal { public static volatile float FloatMinNormal = 1.17549435E-38f; public static volatile float FloatMinDenormal = Single.Epsilon; public static bool IsFlushToZeroEnabled = (FloatMinDenormal == 0); }

- 83,094
- 9
- 75
- 115
Here's how I solved it, with nullable double extension method.
public static bool NearlyEquals(this double? value1, double? value2, double unimportantDifference = 0.0001)
{
if (value1 != value2)
{
if(value1 == null || value2 == null)
return false;
return Math.Abs(value1.Value - value2.Value) < unimportantDifference;
}
return true;
}
...
double? value1 = 100;
value1.NearlyEquals(100.01); // will return false
value1.NearlyEquals(100.000001); // will return true
value1.NearlyEquals(100.01, 0.1); // will return true

- 777
- 6
- 14
Here is a much-expanded version of Simon Hewitt's class:
/// <summary>
/// Safely converts a <see cref="float"/> to an <see cref="int"/> for floating-point comparisons.
/// </summary>
[StructLayout(LayoutKind.Explicit)]
public struct FloatToInt : IEquatable<FloatToInt>, IEquatable<float>, IEquatable<int>, IComparable<FloatToInt>, IComparable<float>, IComparable<int>
{
/// <summary>
/// Initializes a new instance of the <see cref="FloatToInt"/> class.
/// </summary>
/// <param name="floatValue">The <see cref="float"/> value to be converted to an <see cref="int"/>.</param>
public FloatToInt(float floatValue)
: this()
{
FloatValue = floatValue;
}
/// <summary>
/// Gets the floating-point value as an integer.
/// </summary>
[FieldOffset(0)]
public readonly int IntValue;
/// <summary>
/// Gets the floating-point value.
/// </summary>
[FieldOffset(0)]
public readonly float FloatValue;
/// <summary>
/// Indicates whether the current object is equal to another object of the same type.
/// </summary>
/// <returns>
/// true if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.
/// </returns>
/// <param name="other">An object to compare with this object.</param>
public bool Equals(FloatToInt other)
{
return other.IntValue == IntValue;
}
/// <summary>
/// Indicates whether the current object is equal to another object of the same type.
/// </summary>
/// <returns>
/// true if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.
/// </returns>
/// <param name="other">An object to compare with this object.</param>
public bool Equals(float other)
{
return IntValue == new FloatToInt(other).IntValue;
}
/// <summary>
/// Indicates whether the current object is equal to another object of the same type.
/// </summary>
/// <returns>
/// true if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.
/// </returns>
/// <param name="other">An object to compare with this object.</param>
public bool Equals(int other)
{
return IntValue == other;
}
/// <summary>
/// Compares the current object with another object of the same type.
/// </summary>
/// <returns>
/// A value that indicates the relative order of the objects being compared. The return value has the following meanings: Value Meaning Less than zero This object is less than the <paramref name="other"/> parameter.Zero This object is equal to <paramref name="other"/>. Greater than zero This object is greater than <paramref name="other"/>.
/// </returns>
/// <param name="other">An object to compare with this object.</param>
public int CompareTo(FloatToInt other)
{
return IntValue.CompareTo(other.IntValue);
}
/// <summary>
/// Compares the current object with another object of the same type.
/// </summary>
/// <returns>
/// A value that indicates the relative order of the objects being compared. The return value has the following meanings: Value Meaning Less than zero This object is less than the <paramref name="other"/> parameter.Zero This object is equal to <paramref name="other"/>. Greater than zero This object is greater than <paramref name="other"/>.
/// </returns>
/// <param name="other">An object to compare with this object.</param>
public int CompareTo(float other)
{
return IntValue.CompareTo(new FloatToInt(other).IntValue);
}
/// <summary>
/// Compares the current object with another object of the same type.
/// </summary>
/// <returns>
/// A value that indicates the relative order of the objects being compared. The return value has the following meanings: Value Meaning Less than zero This object is less than the <paramref name="other"/> parameter.Zero This object is equal to <paramref name="other"/>. Greater than zero This object is greater than <paramref name="other"/>.
/// </returns>
/// <param name="other">An object to compare with this object.</param>
public int CompareTo(int other)
{
return IntValue.CompareTo(other);
}
/// <summary>
/// Indicates whether this instance and a specified object are equal.
/// </summary>
/// <returns>
/// true if <paramref name="obj"/> and this instance are the same type and represent the same value; otherwise, false.
/// </returns>
/// <param name="obj">Another object to compare to. </param><filterpriority>2</filterpriority>
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
{
return false;
}
if (obj.GetType() != typeof(FloatToInt))
{
return false;
}
return Equals((FloatToInt)obj);
}
/// <summary>
/// Returns the hash code for this instance.
/// </summary>
/// <returns>
/// A 32-bit signed integer that is the hash code for this instance.
/// </returns>
/// <filterpriority>2</filterpriority>
public override int GetHashCode()
{
return IntValue;
}
/// <summary>
/// Implicitly converts from a <see cref="FloatToInt"/> to an <see cref="int"/>.
/// </summary>
/// <param name="value">A <see cref="FloatToInt"/>.</param>
/// <returns>An integer representation of the floating-point value.</returns>
public static implicit operator int(FloatToInt value)
{
return value.IntValue;
}
/// <summary>
/// Implicitly converts from a <see cref="FloatToInt"/> to a <see cref="float"/>.
/// </summary>
/// <param name="value">A <see cref="FloatToInt"/>.</param>
/// <returns>The floating-point value.</returns>
public static implicit operator float(FloatToInt value)
{
return value.FloatValue;
}
/// <summary>
/// Determines if two <see cref="FloatToInt"/> instances have the same integer representation.
/// </summary>
/// <param name="left">A <see cref="FloatToInt"/>.</param>
/// <param name="right">A <see cref="FloatToInt"/>.</param>
/// <returns>true if the two <see cref="FloatToInt"/> have the same integer representation; otherwise, false.</returns>
public static bool operator ==(FloatToInt left, FloatToInt right)
{
return left.IntValue == right.IntValue;
}
/// <summary>
/// Determines if two <see cref="FloatToInt"/> instances have different integer representations.
/// </summary>
/// <param name="left">A <see cref="FloatToInt"/>.</param>
/// <param name="right">A <see cref="FloatToInt"/>.</param>
/// <returns>true if the two <see cref="FloatToInt"/> have different integer representations; otherwise, false.</returns>
public static bool operator !=(FloatToInt left, FloatToInt right)
{
return !(left == right);
}
}

- 7,841
- 4
- 40
- 51
I translated the sample from Michael Borgwardt. This is the result:
public static bool NearlyEqual(float a, float b, float epsilon){
float absA = Math.Abs (a);
float absB = Math.Abs (b);
float diff = Math.Abs (a - b);
if (a == b) {
return true;
} else if (a == 0 || b == 0 || diff < float.Epsilon) {
// a or b is zero or both are extremely close to it
// relative error is less meaningful here
return diff < epsilon;
} else { // use relative error
return diff / (absA + absB) < epsilon;
}
}
Feel free to improve this answer.
-
1This code is incorrect, `float.MinValue` is the minimum value which can be represented by the `float` datatype, not the _smallest positive_ value which can be represented by `float`. – bfair Nov 12 '15 at 19:02
-
Thank you for that important note! I should think more thoroughly before posting ... – testing Nov 13 '15 at 08:37
-
If float.Epsilon is the lowest positive float value, then diff < float.Epsilon will ALWAYS return false, unless the diff is exactly 0, which a == b has already taken care of. – Ultroman the Tacoman Jun 12 '17 at 21:44
I think your second option is the best bet. Generally in floating-point comparison you often only care that one value is within a certain tolerance of another value, controlled by the selection of epsilon.

- 11,755
- 5
- 49
- 77
Although the second option is more general, the first option is better when you have an absolute tolerance, and when you have to execute many of these comparisons. If this comparison is say for every pixel in an image, the multiplication in the second options might slow your execution to unacceptable levels of performance.

- 15,482
- 8
- 63
- 79
-
Peformance isn't a big issue for my applications I'm more concerned with correctness. – Kevin Gale Oct 06 '10 at 16:39
-
1You need to fight your premature optimization instincts harder. Make it work correctly first, only then start even thinking about making it fast (if it's even an issue at all). – Michael Borgwardt Oct 06 '10 at 18:18
if (Math.Abs(1.0 - 1.01) < TOLERANCE) {
//true
}
where TOLERANCE
is the amount you wish to achieve. e.g. TOLERANCE = 0.01
will not result in true. But if you keep it 0.011 it will result in true since the diff is within reach.

- 8,955
- 9
- 67
- 89
static class FloatUtil {
static bool IsEqual(float a, float b, float tolerance = 0.001f) {
return Math.Abs(a - b) < tolerance;
}
static bool IsGreater(float a, float b) {
return a > b;
}
static bool IsLess(float a, float b) {
return a < b;
}
}
The value of tolerance
that is passed into IsEqual
is something that the client could decide.
IsEqual(1.002, 1.001); --> False
IsEqual(1.002, 1.001, 0.01); --> True

- 11,904
- 2
- 71
- 68
Refined the answer given by @EricOuelet above:
//to future maintainers Based on
//to future maintainers
//to future maintainers https://stackoverflow.com/questions/2411392/double-epsilon-for-equality-greater-than-less-than-less-than-or-equal-to-gre
//to future maintainers
public static class DoubleExtensions
{
/// <summary>Compare two doubles taking in account the double precision potential error.</summary>
/// <remarks>
/// This method works fine for floats. That's because all the float values can be represented by a double value,
/// because they're "built" in the same way, just with more possible digits.
/// </remarks>
public static bool AboutEquals(this float value1, double value2) => AboutEquals((double) value1, value2);
/// <summary>Compare two doubles taking in account the double precision potential error.</summary>
/// <remarks>
/// This method works fine for floats. That's because all the float values can be represented by a double value,
/// because they're "built" in the same way, just with more possible digits.
///
/// Also bare in mind that truncation errors accumulate on calculation. The more you do, the more you should increase the epsilon.
/// You get really better performance when you can determine the contextual epsilon first.
/// </remarks>
/// <param name="value1">The first value</param>
/// <param name="value2">The second value</param>
/// <param name="precalculatedContextualEpsilon">The precalculated-contextual-epsilon. Use <see cref="GetContextualEpsilon"/> for this.</param>
/// <returns>true if the two values fall within the given margin, false otherwise</returns>
public static bool AboutEquals(this double value1, double value2, double? precalculatedContextualEpsilon = null)
{
precalculatedContextualEpsilon ??= Math.Max(Math.Abs(value1), Math.Abs(value2)).GetContextualEpsilon();
return Math.Abs(value1 - value2) <= precalculatedContextualEpsilon;
}
public static double GetContextualEpsilon(this double biggestPossibleContextualValue) => biggestPossibleContextualValue * 1E-15;
/// <summary>Matlab equivalent</summary>
/// <param name="dividend"></param>
/// <param name="divisor"></param>
/// <returns></returns>
public static double Mod(this double dividend, double divisor) => dividend - System.Math.Floor(dividend / divisor) * divisor;
}

- 3,786
- 2
- 36
- 56