namespace AntDesignWPF.Utils { using System; using System.Runtime.InteropServices; internal static class DoubleUtil { // Const values // Smallest double value such that 1.0+DBL_EPSILON != 1.0 internal const double DBL_EPSILON = 2.2204460492503131e-016; // Number close to zero, where float.MinValue is -float.MaxValue internal const float FLT_MIN = 1.175494351e-38F; /// /// AreClose - Returns whether or not two doubles are "close". That is, whether or /// not they are within epsilon of each other. Note that this epsilon is proportional /// to the numbers themselves to that AreClose survives scalar multiplication. /// There are plenty of ways for this to return false even for numbers which /// are theoretically identical, so no code calling this should fail to work if this /// returns false. This is important enough to repeat: /// NB: NO CODE CALLING THIS FUNCTION SHOULD DEPEND ON ACCURATE RESULTS - this should be /// used for optimizations *only*. /// /// /// bool - the result of the AreClose comparision. /// /// The first double to compare. /// The second double to compare. public static bool AreClose(double value1, double value2) { //in case they are Infinities (then epsilon check does not work) if (value1 == value2) return true; // This computes (|value1-value2| / (|value1| + |value2| + 10.0)) < DBL_EPSILON var eps = (Math.Abs(value1) + Math.Abs(value2) + 10.0) * DBL_EPSILON; var delta = value1 - value2; return -eps < delta && eps > delta; } /// /// LessThan - Returns whether or not the first double is less than the second double. /// That is, whether or not the first is strictly less than *and* not within epsilon of /// the other number. Note that this epsilon is proportional to the numbers themselves /// to that AreClose survives scalar multiplication. Note, /// There are plenty of ways for this to return false even for numbers which /// are theoretically identical, so no code calling this should fail to work if this /// returns false. This is important enough to repeat: /// NB: NO CODE CALLING THIS FUNCTION SHOULD DEPEND ON ACCURATE RESULTS - this should be /// used for optimizations *only*. /// /// /// bool - the result of the LessThan comparision. /// /// The first double to compare. /// The second double to compare. public static bool LessThan(double value1, double value2) { return value1 < value2 && !AreClose(value1, value2); } /// /// GreaterThan - Returns whether or not the first double is greater than the second double. /// That is, whether or not the first is strictly greater than *and* not within epsilon of /// the other number. Note that this epsilon is proportional to the numbers themselves /// to that AreClose survives scalar multiplication. Note, /// There are plenty of ways for this to return false even for numbers which /// are theoretically identical, so no code calling this should fail to work if this /// returns false. This is important enough to repeat: /// NB: NO CODE CALLING THIS FUNCTION SHOULD DEPEND ON ACCURATE RESULTS - this should be /// used for optimizations *only*. /// /// /// bool - the result of the GreaterThan comparision. /// /// The first double to compare. /// The second double to compare. public static bool GreaterThan(double value1, double value2) { return value1 > value2 && !AreClose(value1, value2); } /// /// IsOne - Returns whether or not the double is "close" to 1. Same as AreClose(double, 1), /// but this is faster. /// /// /// bool - the result of the AreClose comparision. /// /// The double to compare to 1. public static bool IsOne(double value) { return Math.Abs(value - 1.0) < 10.0 * DBL_EPSILON; } /// /// IsZero - Returns whether or not the double is "close" to 0. Same as AreClose(double, 0), /// but this is faster. /// /// /// bool - the result of the AreClose comparision. /// /// The double to compare to 0. public static bool IsZero(double value) { return Math.Abs(value) < 10.0 * DBL_EPSILON; } [StructLayout(LayoutKind.Explicit)] private struct NanUnion { [FieldOffset(0)] internal double DoubleValue; [FieldOffset(0)] internal ulong UintValue; } // The standard CLR double.IsNaN() function is approximately 100 times slower than our own wrapper, // so please make sure to use DoubleUtil.IsNaN() in performance sensitive code. // PS item that tracks the CLR improvement is DevDiv Schedule : 26916. // IEEE 754 : If the argument is any value in the range 0x7ff0000000000001L through 0x7fffffffffffffffL // or in the range 0xfff0000000000001L through 0xffffffffffffffffL, the result will be NaN. public static bool IsNaN(double value) { var t = new NanUnion { DoubleValue = value }; var exp = t.UintValue & 0xfff0000000000000; var man = t.UintValue & 0x000fffffffffffff; return (exp == 0x7ff0000000000000 || exp == 0xfff0000000000000) && man != 0; } } }