#region CPL License /* Nuclex Framework Copyright (C) 2002-2014 Nuclex Development Labs This library is free software; you can redistribute it and/or modify it under the terms of the IBM Common Public License as published by the IBM Corporation; either version 1.0 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the IBM Common Public License for more details. You should have received a copy of the IBM Common Public License along with this library */ #endregion using System; using System.Collections.Generic; using System.Runtime.InteropServices; namespace Nuclex.Support { /// Helper routines for working with floating point numbers /// /// /// The floating point comparison code is based on this excellent article: /// http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm /// /// /// "ULP" means Unit in the Last Place and in the context of this library refers to /// the distance between two adjacent floating point numbers. IEEE floating point /// numbers can only represent a finite subset of natural numbers, with greater /// accuracy for smaller numbers and lower accuracy for very large numbers. /// /// /// If a comparison is allowed "2 ulps" of deviation, that means the values are /// allowed to deviate by up to 2 adjacent floating point values, which might be /// as low as 0.0000001 for small numbers or as high as 10.0 for large numbers. /// /// public static class FloatHelper { #region struct FloatIntUnion /// Union of a floating point variable and an integer [StructLayout(LayoutKind.Explicit)] private struct FloatIntUnion { /// The union's value as a floating point variable [FieldOffset(0)] public float Float; /// The union's value as an integer [FieldOffset(0)] public int Int; /// The union's value as an unsigned integer [FieldOffset(0)] public uint UInt; } #endregion // struct FloatIntUnion #region struct DoubleLongUnion /// Union of a double precision floating point variable and a long [StructLayout(LayoutKind.Explicit)] private struct DoubleLongUnion { /// The union's value as a double precision floating point variable [FieldOffset(0)] public double Double; /// The union's value as a long [FieldOffset(0)] public long Long; /// The union's value as an unsigned long [FieldOffset(0)] public ulong ULong; } #endregion // struct DoubleLongUnion /// A floating point value that holds a positive zero public const float PositiveZeroFloat = +0.0f; /// A floating point value that holds a negative zero /// /// Negative zeros have a special representation in IEEE 752 floating point math /// public const float NegativeZeroFloat = -0.0f; /// A double precision floating point value that holds a positive zero public const double PositiveZeroDouble = +0.0; /// A doublep precision floating point value that holds a negative zero /// /// Negative zeros have a special representation in IEEE 752 floating point math /// public const double NegativeZeroDouble = -0.0; /// Checks whether the floating point value is exactly zero /// Value that will be checked for being zero /// True if the value is zero, false otherwise public static bool IsZero(float value) { return (value == PositiveZeroFloat) || (value == NegativeZeroFloat); } /// /// Checks whether the double precision floating point value is exactly zero /// /// Value that will be checked for being zero /// True if the value is zero, false otherwise public static bool IsZero(double value) { return (value == PositiveZeroDouble) || (value == NegativeZeroDouble); } /// Compares two floating point values for equality /// First floating point value to be compared /// Second floating point value t be compared /// /// Maximum number of representable floating point values that are allowed to /// be between the left and the right floating point values /// /// True if both numbers are equal or close to being equal /// /// /// Floating point values can only represent a finite subset of natural numbers. /// For example, the values 2.00000000 and 2.00000024 can be stored in a float, /// but nothing inbetween them. /// /// /// This comparison will count how many possible floating point values are between /// the left and the right number. If the number of possible values between both /// numbers is less than or equal to maxUlps, then the numbers are considered as /// being equal. /// /// /// Implementation partially follows the code outlined here (link now defunct): /// http://www.anttirt.net/2007/08/19/proper-floating-point-comparisons/ /// And here: /// http://www.altdevblogaday.com/2012/02/22/comparing-floating-point-numbers-2012-edition/ /// /// public static bool AreAlmostEqual(float left, float right, int maxUlps) { var leftUnion = new FloatIntUnion(); var rightUnion = new FloatIntUnion(); leftUnion.Float = left; rightUnion.Float = right; if(leftUnion.Int < 0) { leftUnion.Int = unchecked((int)0x80000000 - leftUnion.Int); } if(rightUnion.Int < 0) { rightUnion.Int = unchecked((int)0x80000000 - rightUnion.Int); } return Math.Abs(rightUnion.Int - leftUnion.Int) <= maxUlps; } #if false public static bool OldAreAlmostEqual(float left, float right, int maxUlps) { FloatInt32Union leftUnion = new FloatInt32Union(); FloatInt32Union rightUnion = new FloatInt32Union(); leftUnion.Float = left; rightUnion.Float = right; uint leftSignMask = (leftUnion.UInt >> 31); uint rightSignMask = (rightUnion.UInt >> 31); uint leftTemp = ((0x80000000 - leftUnion.UInt) & leftSignMask); leftUnion.UInt = leftTemp | (leftUnion.UInt & ~leftSignMask); uint rightTemp = ((0x80000000 - rightUnion.UInt) & rightSignMask); rightUnion.UInt = rightTemp | (rightUnion.UInt & ~rightSignMask); return (Math.Abs(leftUnion.Int - rightUnion.Int) <= maxUlps); } #endif /// Compares two double precision floating point values for equality /// First double precision floating point value to be compared /// Second double precision floating point value t be compared /// /// Maximum number of representable double precision floating point values that are /// allowed to be between the left and the right double precision floating point values /// /// True if both numbers are equal or close to being equal /// /// /// Double precision floating point values can only represent a limited series of /// natural numbers. For example, the values 2.0000000000000000 and 2.0000000000000004 /// can be stored in a double, but nothing inbetween them. /// /// /// This comparison will count how many possible double precision floating point /// values are between the left and the right number. If the number of possible /// values between both numbers is less than or equal to maxUlps, then the numbers /// are considered as being equal. /// /// /// Implementation partially follows the code outlined here: /// http://www.anttirt.net/2007/08/19/proper-floating-point-comparisons/ /// And here: /// http://www.altdevblogaday.com/2012/02/22/comparing-floating-point-numbers-2012-edition/ /// /// public static bool AreAlmostEqual(double left, double right, long maxUlps) { var leftUnion = new DoubleLongUnion(); var rightUnion = new DoubleLongUnion(); leftUnion.Double = left; rightUnion.Double = right; if(leftUnion.Long < 0) { leftUnion.Long = unchecked((long)0x8000000000000000 - leftUnion.Long); } if(rightUnion.Long < 0) { rightUnion.Long = unchecked((long)0x8000000000000000 - rightUnion.Long); } return Math.Abs(rightUnion.Long - leftUnion.Long) <= maxUlps; } #if false public static bool OldAreAlmostEqual(double left, double right, long maxUlps) { DoubleInt64Union leftUnion = new DoubleInt64Union(); DoubleInt64Union rightUnion = new DoubleInt64Union(); leftUnion.Double = left; rightUnion.Double = right; ulong leftSignMask = (leftUnion.ULong >> 63); ulong rightSignMask = (rightUnion.ULong >> 63); ulong leftTemp = ((0x8000000000000000 - leftUnion.ULong) & leftSignMask); leftUnion.ULong = leftTemp | (leftUnion.ULong & ~leftSignMask); ulong rightTemp = ((0x8000000000000000 - rightUnion.ULong) & rightSignMask); rightUnion.ULong = rightTemp | (rightUnion.ULong & ~rightSignMask); return (Math.Abs(leftUnion.Long - rightUnion.Long) <= maxUlps); } #endif /// /// Reinterprets the memory contents of a floating point value as an integer value /// /// /// Floating point value whose memory contents to reinterpret /// /// /// The memory contents of the floating point value interpreted as an integer /// public static int ReinterpretAsInt(this float value) { FloatIntUnion union = new FloatIntUnion(); union.Float = value; return union.Int; } /// /// Reinterprets the memory contents of a double precision floating point /// value as an integer value /// /// /// Double precision floating point value whose memory contents to reinterpret /// /// /// The memory contents of the double precision floating point value /// interpreted as an integer /// public static long ReinterpretAsLong(this double value) { DoubleLongUnion union = new DoubleLongUnion(); union.Double = value; return union.Long; } /// /// Reinterprets the memory contents of an integer as a floating point value /// /// Integer value whose memory contents to reinterpret /// /// The memory contents of the integer value interpreted as a floating point value /// public static float ReinterpretAsFloat(this int value) { FloatIntUnion union = new FloatIntUnion(); union.Int = value; return union.Float; } /// /// Reinterprets the memory contents of an integer value as a double precision /// floating point value /// /// Integer whose memory contents to reinterpret /// /// The memory contents of the integer interpreted as a double precision /// floating point value /// public static double ReinterpretAsDouble(this long value) { DoubleLongUnion union = new DoubleLongUnion(); union.Long = value; return union.Double; } } } // namespace Nuclex.Support