Floating point comparison functions for C #

Can someone point out (or show) some good general floating point comparison functions in C # to compare floating point values? I want to implement functions for IsEqual , IsGreater a IsLess . I also take great care that doubles do not swim.

+64
floating-point c #
Oct. 06 '10 at 16:14
source share
11 answers

Writing a useful general purpose floating point IsEqual very, very difficult, if not simply impossible. Your current code will fail if a==0 . The way the method should behave in such cases is actually a matter of determination, and perhaps the code will be best suited for the specific use of the domain.

For this kind of thing, you really need a good set of tests. Here's how I did it for the Floating-Point Guide , here's what I came up with at the end (Java code should be simple 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 a test kit on the site .

Appendix: the same code in C # for paired characters (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; } } 
+67
Oct 06 2018-10-06
source share

From Bruce Dawson's article on comparing floats , you can also compare float as integers. Proximity is determined by the 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: Bit converter is relatively slow. If you want to use unsafe code, then this 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 ); } 
+23
Oct 06 2018-10-06
source share

In addition to Andrew Wang, answer: if the BitConverter method is too slow, but you cannot use unsafe code in your project, this structure is ~ 6 times faster 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; } 

(By the way, I tried to use the decision made, but he (like my conversion) could not perform some of the unit tests mentioned in the answer, for example, assertTrue(nearlyEqual(Float.MIN_VALUE, -Float.MIN_VALUE)); )

+11
Jul 13 '11 at 11:22
source share

Continuing the answers provided by Michael and testing , it is important to keep in mind when translating Java source code for C # that Java and C # define their constants differently. C #, for example, lacks Java MIN_NORMAL, and the definitions for MinValue vary greatly.

Java defines MIN_VALUE as the smallest possible positive value, and C # defines it as the smallest possible representable value in general. The equivalent value in C # is Epsilon.

The absence of MIN_NORMAL is problematic for direct translation of the original algorithm - without it, things begin to break down for small values ​​near zero. Java MIN_NORMAL follows the IEEE specification of the smallest number possible, without a leading bit of value as zero, and with that in mind, we can define our own normals for singles and doubles (which dbc mentions in the comments on the original answer).

The following C # code for singles transmits all tests specified in the Floating Point Guide, and the dual edition transmits all tests with minor changes in test cases to allow for increased accuracy.

 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, except for type changes, and it is usually defined as normal.

 const double doubleNormal = (1L << 52) * double.Epsilon; 
+7
Jun 04 '17 at 2:51 on
source share

Be careful with some answers ...

UPDATE 2019-0829, I also included decompiled Microsoft code, which should be much better than mine.

1 - You can easily imagine any number with 15 significant digits in double memory. See Wikipedia .

2 - The problem arises from the calculation of floating numbers, where you may lose some accuracy. I mean, after calculating, a number of type .1 can become something like .1000000000000001 ==>. When you do some calculations, the results can be truncated to be presented in double. This truncation brings an error that you may get.

3 - To avoid the problem of comparing double values, people introduce an error, often called epsilon. If two floating numbers have only contextual epsilon as a difference, then they are considered equal. double.Epsilon is the smallest number between a double value and its nearest (next or previous) value.

4 - The difference between two double values ​​may be greater than double.epsilon. The difference between a valid double value and a calculated one depends on how many calculations you made and which ones. Many peoples believe that it is always double. Epsilon, but they are really wrong. To get an excellent answer, see Hans Passant's answer . Epsilon is based on your context, where it depends on the highest number that you achieve during the calculations, and on the number of calculations you do (a truncation error accumulates).

5 is the code I'm using. Be careful, I only use my epsilon for a few calculations. Otherwise, I multiply my epsilon by 10 or 100.

6 - As SvenL notes, perhaps my epsilon is not large enough. I suggest reading the SvenL comment. Also, maybe decimal can do the job for your case?

Decompiled Microsoft 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(rX) || DoubleUtil.IsNaN(rY) || (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 is:

 public static class DoubleExtension { // ****************************************************************** // Base on Hans Passant Answer on: // /questions/105706/doubleepsilon-for-equality-greater-than-less-than-less-than-or-equal-to-greater-than-or-equal-to /// <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: // /questions/105706/doubleepsilon-for-equality-greater-than-less-than-less-than-or-equal-to-greater-than-or-equal-to /// <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; } // ****************************************************************** } 
+6
Feb 15 '16 at 20:34
source share

Here is an extended version of the Simon Hewitt 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); } } 
+4
Dec 23 '11 at 16:08
source share

This is how I solved it using the NULL 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 
+4
Apr 08 '14 at 8:44
source share

Although the second option is more general, the first option is better if you have absolute tolerance, and when you need to perform many of these comparisons. If this comparison affects for each pixel in the image, multiplication in the second options can slow down execution to unacceptable performance levels.

+1
Oct. 06 '10 at 16:23
source share

How about: b - delta < a && a < b + delta

+1
Oct 06 '10 at 16:26
source share

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.

+1
Jul 23 '15 at 12:41
source share

I think your second option is the best choice. As a rule, in comparison with floating point, you often only have to leave one value within a certain tolerance of another value, controlled by the choice of epsilon.

0
Oct 06 '10 at 16:17
source share



All Articles