C# double好用的扩展

了解过double在计算机中的存储方式的,应该知道double类型,要判断"相等"是很困难的,比如我们写出下面一行代码

if(a==b){//do something...}

此时编译器会提示这个判断不严谨,他推荐:

Math.Abs(a-b)<一个很小的数

于是乎,我在搜索引擎中搜索了一下,发现类似这种double类型的比较操作被封装成了工具方法,下面给出一个从lindexi的一篇博客中Copy来的工具类,以后可以放到自己的项目中使用。

/// <summary>
/// Double 的扩展
/// </summary>
//SOURCE: https://github.com/mathnet/mathnet-numerics/blob/master/src/Numerics/Precision.cs
//        https://github.com/mathnet/mathnet-numerics/blob/master/src/Numerics/Precision.Equality.cs
//        http://referencesource.microsoft.com/#WindowsBase/Shared/MS/Internal/DoubleUtil.cs
//        http://stackoverflow.com/questions/2411392/double-epsilon-for-equality-greater-than-less-than-less-than-or-equal-to-gre
public static class DoubleExtensions
{
    /// <summary>
    /// The smallest positive number that when SUBTRACTED from 1D yields a result different from 1D.
    ///
    /// This number has the following properties:
    ///     (1 - NegativeMachineEpsilon) &lt; 1 and
    ///     (1 + NegativeMachineEpsilon) == 1
    /// </summary>
    public static readonly double MeasuredNegativeMachineEpsilon = MeasureNegativeMachineEpsilon();

    /// <summary>
    /// The smallest positive number that when ADDED to 1D yields a result different from 1D.
    ///
    /// This number has the following properties:
    ///     (1 - PositiveDoublePrecision) &lt; 1 and
    ///     (1 + PositiveDoublePrecision) &gt; 1
    /// </summary>
    public static readonly double MeasuredPositiveMachineEpsilon = MeasurePositiveMachineEpsilon();


    /// <summary>
    /// The smallest positive number that when SUBTRACTED from 1D yields a result different from 1D.
    /// The value is derived from 2^(-53) = 1.1102230246251565e-16, where IEEE 754 binary64 &quot;double precision&quot; floating point numbers have a significand precision that utilize 53 bits.
    ///
    /// This number has the following properties:
    ///     (1 - NegativeMachineEpsilon) &lt; 1 and
    ///     (1 + NegativeMachineEpsilon) == 1
    /// </summary>
    public const double NegativeMachineEpsilon = 1.1102230246251565e-16D; //Math.Pow(2, -53);

    /// <summary>
    /// The smallest positive number that when ADDED to 1D yields a result different from 1D.
    /// The value is derived from 2 * 2^(-53) = 2.2204460492503131e-16, where IEEE 754 binary64 &quot;double precision&quot; floating point numbers have a significand precision that utilize 53 bits.
    ///
    /// This number has the following properties:
    ///     (1 - PositiveDoublePrecision) &lt; 1 and
    ///     (1 + PositiveDoublePrecision) &gt; 1
    /// </summary>
    public const double PositiveMachineEpsilon = 2D * NegativeMachineEpsilon;

    public static bool IsClose(this double value1, double value2,
        double maximumAbsoluteError = DefaultDoubleAccuracy)
    {
        if (double.IsInfinity(value1) || double.IsInfinity(value2))
        {
            return Equals(value1, value2);
        }

        if (double.IsNaN(value1) || double.IsNaN(value2))
        {
            return false;
        }

        var delta = value1 - value2;

        //return Math.Abs(delta) <= maximumAbsoluteError;

        if (delta > maximumAbsoluteError ||
            delta < -maximumAbsoluteError)
        {
            return false;
        }

        return true;
    }

    public static bool LessThan(this double value1, double value2)
    {
        return (value1 < value2) && !IsClose(value1, value2);
    }

    public static bool GreaterThan(this double value1, double value2)
    {
        return (value1 > value2) && !IsClose(value1, value2);
    }

    public static bool LessThanOrClose(this double value1, double value2)
    {
        return (value1 < value2) || IsClose(value1, value2);
    }

    public static bool GreaterThanOrClose(this double value1, double value2)
    {
        return (value1 > value2) || IsClose(value1, value2);
    }

    public static bool IsOne(this double value)
    {
        var delta = value - 1D;

        //return Math.Abs(delta) <= PositiveMachineEpsilon;

        if (delta > PositiveMachineEpsilon ||
            delta < -PositiveMachineEpsilon)
        {
            return false;
        }

        return true;
    }

    public static bool IsZero(this double value)
    {
        //return Math.Abs(value) <= PositiveMachineEpsilon;

        if (value > PositiveMachineEpsilon ||
            value < -PositiveMachineEpsilon)
        {
            return false;
        }

        return true;
    }

    /// <summary>
    /// 判断两个 <see cref="T:System.Double" /> 值是否近似相等。
    /// </summary>
    /// <param name="d1">值1。</param>
    /// <param name="d2">值2。</param>
    /// <param name="tolerance">近似容差。</param>
    [PublicAPI]
    public static bool NearlyEquals(double d1, double d2, double tolerance = 1E-05)
    {
        return IsClose(d1, d2, tolerance);
    }

    private static double MeasureNegativeMachineEpsilon()
    {
        var epsilon = 1D;

        do
        {
            var nextEpsilon = epsilon / 2D;

            if (NearlyEquals(1D - nextEpsilon, 1D)) //if nextEpsilon is too small
            {
                return epsilon;
            }

            epsilon = nextEpsilon;
        } while (true);
    }

    private static double MeasurePositiveMachineEpsilon()
    {
        var epsilon = 1D;

        do
        {
            var nextEpsilon = epsilon / 2D;

            if (NearlyEquals((1D + nextEpsilon), 1D)) //if nextEpsilon is too small
            {
                return epsilon;
            }

            epsilon = nextEpsilon;
        } while (true);
    }

    private const double DefaultDoubleAccuracy = NegativeMachineEpsilon * 10D;
}

参考:C# double 好用的扩展

posted @ 2025-06-07 20:26  BigBosscyb  阅读(11)  评论(0)    收藏  举报