Re: How to overload == for System.Double

Alan Baljeu <[email protected]> Fri, 2 Feb 2007 10:27:17 -0500
Newsgroups gmane.comp.windows.devel.dotnet.cx
Message-ID <086d01c746de$a07c1940$1dc80ac6@CTI>
On some architectures, the same float can have different representations, and similar floats
can have quite different representations.  Given that, is this implementation totally
reliable, and has it been seriously tested?  Besides that, is it really much better than
Math.Abs(a-b) < 1e-6 (or whatever tolerance you want to apply)?

> -----Original Message-----
> From: Discussion relating to the specifics of the C# and
> Managed C++ languages [mailto:[email protected]]
> On Behalf Of Marc Brooks
> Sent: Thursday, February 01, 2007 5:13 PM
> To: [email protected]
> Subject: Re: [DOTNET-CX] How to overload == for System.Double
>
> > Is it possible?
>
> No.  Operators are specially recognized, and changing the
> behavior would be wrond anyway.  Use this:
>
>         public static partial class Utilities
>         {
>                 /// <summary>
>                 /// Allows reinterpretation conversion
> doubles to/from longs AND floats to/from ints to allow direct
>                 /// manipulation of the bit patterns.  Similar to
> BitConverter.DoubleToInt64 and
>                 /// BitConverter.Int64ToDouble, but also
> works for floats/ints. Uses a clever trick of explicit
>                 /// struct layout to insure that the same
> memory is used for all values suggested by Jeroen Frijters
>                 /// see
> http://groups.yahoo.com/group/win_tech_off_topic/message/31670
>                 /// </summary>
>
> [System.Runtime.InteropServices.StructLayout(System.Runtime.In
> teropServices.LayoutKind.Explicit)]
>                 private struct BitReinterpreter
>                 {
>                         public static int Convert(float f)
>                         {
>                                 BitReinterpreter br = new
> BitReinterpreter(f);
>                                 return br.i;
>                         }
>
>                         public static float Convert(int i)
>                         {
>                                 BitReinterpreter br = new
> BitReinterpreter(i);
>                                 return br.f;
>                         }
>
>                         public static long Convert(double d)
>                         {
>                                 BitReinterpreter br = new
> BitReinterpreter(d);
>                                 return br.l;
>                         }
>
>                         public static double Convert(long l)
>                         {
>                                 BitReinterpreter br = new
> BitReinterpreter(l);
>                                 return br.d;
>                         }
>
>
> [System.Runtime.InteropServices.FieldOffset(0)]
>                         float f;
>
> [System.Runtime.InteropServices.FieldOffset(0)]
>                         int i;
>
> [System.Runtime.InteropServices.FieldOffset(0)]
>                         double d;
>
> [System.Runtime.InteropServices.FieldOffset(0)]
>                         long l;
>
>                         private BitReinterpreter(float f)
>                         {
>                                 this.l = 0;
>                                 this.d = 0;
>                                 this.i = 0;
>                                 this.f = f;
>                         }
>
>                         private BitReinterpreter(int i)
>                         {
>                                 this.d = 0;
>                                 this.l = 0;
>                                 this.f = 0;
>                                 this.i = i;
>                         }
>
>                         private BitReinterpreter(double d)
>                         {
>                                 this.i = 0;
>                                 this.f = 0;
>                                 this.l = 0;
>                                 this.d = d;
>                         }
>
>                         private BitReinterpreter(long l)
>                         {
>                                 this.f = 0;
>                                 this.i = 0;
>                                 this.d = 0;
>                                 this.l = l;
>                         }
>                 }
>
>                 /// <summary>
>                 /// Compared two single precision
> floating-point values to see if they are equal within a
>                 /// designated precision specified as the
> number of digits in the last significant place
>                 /// </summary>
>                 /// <param name="left">The first value</param>
>                 /// <param name="right">The second value</param>
>                 /// <param name="maxUnitsInLastPlace">The
> maximum error in terms of Units in the Last Place.
>                 /// This specifies how big an error we are
> willing to accept in terms of the value of the
>                 /// least significant digit of the floating
> point number's representation.
>                 /// It can also be interpreted in terms of
> how many representable doubles we are willing
>                 /// to accept between left and right. This
> function will allow maxUnitsInLastPlace - 1 floats
>                 /// between left and right.</param>
>                 /// <returns>True if left and right are equal
> or nearly equal. False if not nearly equal OR if
>                 /// either value is a NaN.</returns>
>                 public static bool AlmostEqual(float left,
> float right, int
> maxUnitsInLastPlace)
>                 {
>                         // If left or right are infinity
> (positive or negative) then
>                         // only return true if they are
> exactly equal to each other -
>                         // that is, if they are both
> infinities of the same sign.
>                         if (float.IsInfinity(left) ||
> float.IsInfinity(right))
>                                 return left == right;
>
>                         // If left or right are a NAN, return
> false. NANs are equal to nothing,
>                         // not even themselves.
>                         if (float.IsNaN(left) || float.IsNaN(right))
>                                 return false;
>
>                         // The check for left == right is
> because zero and negative zero have different
>                         // signs but are equal to each other.
>                         if (Math.Sign(left) != Math.Sign(right))
>                                 return left == right;
>
>                         int aInt = BitReinterpreter.Convert(left);
>
>                         // Make aInt lexicographically
> ordered as a twos-complement int
>                         if (aInt < 0)
>                                 aInt = (int)(0x80000000 - (uint)aInt);
>
>                         int bInt = BitReinterpreter.Convert(right);
>
>                         // Make bInt lexicographically
> ordered as a twos-complement int
>                         if (bInt < 0)
>                                 bInt = (int)(0x80000000 - (uint)bInt);
>
>                         // Now we can compare aInt and bInt
> to find out how far apart left and right
>                         // are.
>                         int intDiff = Math.Abs(aInt - bInt);
>
>                         if (intDiff <= maxUnitsInLastPlace)
>                                 return true;
>
>                         return false;
>                 }
>
>                 /// <summary>
>                 /// Compared two double precision
> floating-point values to see if they are equal within a
>                 /// designated precision specified as the
> number of digits in the last significant place
>                 /// </summary>
>                 /// <param name="left">The first value</param>
>                 /// <param name="right">The second value</param>
>                 /// <param name="maxUnitsInLastPlace">The
> maximum error in terms of Units in the Last Place.
>                 /// This specifies how big an error we are
> willing to accept in terms of the value of the
>                 /// least significant digit of the floating
> point number's representation.
>                 /// It can also be interpreted in terms of
> how many representable doubles we are willing
>                 /// to accept between left and right. This
> function will allow maxUnitsInLastPlace - 1 floats
>                 /// between left and right.</param>
>                 /// <returns>True if left and right are equal
> or nearly equal. False if not nearly equal OR if
>                 /// either value is a NaN.</returns>
>                 public static bool AlmostEqual(double left,
> double right, long
> maxUnitsInLastPlace)
>                 {
>                         // If left or right are infinity
> (positive or negative) then
>                         // only return true if they are
> exactly equal to each other -
>                         // that is, if they are both
> infinities of the same sign.
>                         if (double.IsInfinity(left) ||
> double.IsInfinity(right))
>                                 return left == right;
>
>                         // If left or right are a NAN, return
> false. NANs are equal to nothing,
>                         // not even themselves.
>                         if (double.IsNaN(left) || double.IsNaN(right))
>                                 return false;
>
>                         // The check for left == right is
> because zero and negative zero have different
>                         // signs but are equal to each other.
>                         if (Math.Sign(left) != Math.Sign(right))
>                                 return left == right;
>
>                         long aInt = BitReinterpreter.Convert(left);
>
>                         // Make aInt lexicographically
> ordered as a twos-complement int
>                         if (aInt < 0)
>                                 aInt =
> (long)(0x8000000000000000 - (ulong)aInt);
>
>                         long bInt = BitReinterpreter.Convert(right);
>
>                         // Make bInt lexicographically
> ordered as a twos-complement int
>                         if (bInt < 0)
>                                 bInt =
> (long)(0x8000000000000000 - (ulong)bInt);
>
>                         // Now we can compare aInt and bInt
> to find out how far apart A and B
>                         // are.
>                         long intDiff = Math.Abs(aInt - bInt);
>
>                         if (intDiff <= maxUnitsInLastPlace)
>                                 return true;
>
>                         return false;
>                 }
>         }
>
> --
> "I am Dyslexic of Borg. Resistors are fertile. Prepare to
> have your ass laminated." -- Dan Nitschke
>
> Marc C. Brooks
> http://musingmarc.blogspot.com
>
> ===================================
> This list is hosted by DevelopMentorR  http://www.develop.com
>
> View archives and manage your subscription(s) at
> http://discuss.develop.com

===================================
This list is hosted by DevelopMentorĀ®  http://www.develop.com

View archives and manage your subscription(s) at http://discuss.develop.com