在很多代码需要使用数学计算,在用到 double 很难直接判断一个值是 0 或者 1 ,判断两个值相等。

本文提供一个数学扩展,让大家可以简单使用到 double 判断

在开始看本文之前,希望大家是知道计算机是如何存放 double 和 double 精度问题原因。如果大家不知道这个的话,会比较难理解为什么需要使用扩展方法来判断。

如果只是想用这个类,请把到文章最后面,复制代码到自己项目。

例如有两个计算出来的 double ,分别是 a 和 b ,如果直接判断相等,那么 Resharper 会不开心,告诉你这个代码可能判断不对。

 a == b

如果你问 Resharper 建议修改为怎样,他会告诉你,修改为这样

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

原因就是 double 精度问题,虽然你觉得使用两个相同的方法计算出来的数值在数学计算上是相等的,但是实际上在进行判断的时候判断是不相等。

请注意,只有赋值的 double 才可以进行自带的判断相等,如果是计算拿到的 double ,使用自带的判断相等可能会把两个相同的 double 判断为不相同。

可以看到上面的代码,如果用到很多地方判断两个值就会有很多冗余的代码,而且在 Math.Abs 求绝对值计算性能是比不过判断一个大于 0 的值和一个小于 0 的值做两次判断

一个比较建议的判断两个 double 是不是相等的方法是判断两个值的大小

        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;
}

这个方法不敢写判断相等,因为实际上有一些值是在数学上计算不相等,但是在这里判断是相等。

刚刚写了和另一个 double 判断相等,那么如何判断 double 是不是 0?虽然可以直接把 0 作为 double 判断,但是实际上这个判断是不建议的,因为有更好的方法。

在 double 计算,最小的一个单位可以让 1 加上这个值就不等于 1 的就是 2 * 2^(-53),代码把这个这个值变量写为 PositiveMachineEpsilon ,使用这个 PositiveMachineEpsilon 可以判断一个 double 的大小。

判断一个 double 是 0 那么可以通过判断这个值大于 -PositiveMachineEpsilon 并且 小于PositiveMachineEpsilon

        public static bool IsZero(this double value)
{
//return Math.Abs(value) <= PositiveMachineEpsilon; if (value > PositiveMachineEpsilon ||
value < -PositiveMachineEpsilon)
{
return false;
} return true;
}

那么如何判断 double 是 1 ?如果有仔细看上面的代码,那么很容易就知道如何判断

        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;
}

这个代码是从 https://github.com/mathnet/mathnet-numerics/blob/master/src/Numerics/Precision.cs 复制

    /// <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;
}

参见:

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

我搭建了自己的博客 https://blog.lindexi.com/ 欢迎大家访问,里面有很多新的博客。只有在我看到博客写成熟之后才会放在csdn或博客园,但是一旦发布了就不再更新

如果在博客看到有任何不懂的,欢迎交流,我搭建了 dotnet 职业技术学院 欢迎大家加入


本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。欢迎转载、使用、重新发布,但务必保留文章署名林德熙(包含链接:http://blog.csdn.net/lindexi_gd ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系

C# double 好用的扩展的更多相关文章

  1. 6.Swift协议|扩展|访问权限|异常调试|类型转换|运算函数|ARC|类类型初试化器|值类型初始化器

    1. 协议(Protocol):与OC之间唯一不同的是Swift中的协议不管是属性还时方法全部是必须实现的 /** protocol*/ protocol FullNamed { /** 计算属性申明 ...

  2. 2019-3-1-C#-double-好用的扩展

    title author date CreateTime categories C# double 好用的扩展 lindexi 2019-3-1 9:19:5 +0800 2018-05-15 10: ...

  3. 我的MYSQL学习心得(四) 数据类型

    我的MYSQL学习心得(四) 数据类型 我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得(五) 运 ...

  4. 吉特仓库管理系统-ORM框架的使用

    最近在园子里面连续看到几篇关于ORM的文章,其中有两个印象比较深刻<<SqliteSugar>>,另外一篇文章是<<我的开发框架之ORM框架>>, 第一 ...

  5. Matlab中的数据类型

    Matlab中有15种基本数据类型,主要是整型.浮点.逻辑.字符.日期和时间.结构数组.单元格数组以及函数句柄等.         1.整型:(int8:uint8:int16:uint16:int3 ...

  6. [精校版]The Swift Programming Language

    通常来说,编程语言教程中的第一个程序应该在屏幕上打印“Hello, world”.在 Swift 中,可以用一行代码实现:  println("hello, world")   如 ...

  7. 李洪强iOS开发之-Swift_00_介绍

    SWIFT (计算机编程语言) Swift,苹果于2014年WWDC(苹果开发者大会)发布的新开发语言,可与Objective-C*共同运行于Mac OS和iOS平台,用于搭建基于苹果平台的应用程序. ...

  8. Matlab基本数据类型

    本文转载自:http://hi.baidu.com/xmf6227/blog/item/97ca2ddf98f1b61f495403cb.html Matlab中有15种基本数据类型,主要是整型.浮点 ...

  9. Swift 初见

    http://numbbbbb.gitbooks.io/-the-swift-programming-language-/chapter1/02_a_swift_tour.html 本页内容包括: 简 ...

随机推荐

  1. pycahrm安装说明

    从官网下载http://www.jetbrains.com/pycharm/download/other.html(PS:现在需要翻墙可以直接用文件夹里的那个) 下载完成后双击文件 第二步:双击已下载 ...

  2. linux下清除tomcat缓存

    进入tomcat/bin目录下,执行命令:./shutdown.sh 然后执行命令:ps -aux ,查看tomcat是否真的关闭了 如果没有关闭则执行命令:kill -9 #pid来彻底关闭tomc ...

  3. 【Leetcode链表】合并两个有序链表(21)

    题目 将两个有序链表合并为一个新的有序链表并返回.新链表是通过拼接给定的两个链表的所有节点组成的. 示例: 输入:1->2->4, 1->3->4 输出:1->1-> ...

  4. ssh 出错 Permission denied (publickey,password).

    将客户端的~/.ssh/know_hosts 文件删掉试试 ssh debug信息 ssh -vvv xxx@192.168.1.111

  5. iOS 11 适配UIWebView,页面下移20的问题

    方案1: AppDelegate文件 didFinishLaunchingWithOptions()中添加如下代码 if (@available(iOS 11.0, *)) { [[UIScrollV ...

  6. python 字典创建

  7. SDUT-3335_数据结构实验之栈与队列八:栈的基本操作

    数据结构实验之栈与队列八:栈的基本操作 Time Limit: 1000 ms Memory Limit: 65536 KiB Problem Description 堆栈是一种基本的数据结构.堆栈具 ...

  8. 巧用 PHP 数组函数

    0x00 前言 PHP 的数组是一种很强大的数据类型,与此同时 PHP 内置了一系列与数组相关的函数可以很轻易的实现日常开发的功能.但是我发现好像很多小伙伴都忽略了内置函数的作用(比如我自己就编写过一 ...

  9. es6 promise简析

    1.Promise的含义 所谓Promise,就是一个对象,用来传递异步操作的消息. Promise对象有以下两个特点: 对象的状态不受外界影响.Promise对象代表一个异步操作,有三种状态:Pen ...

  10. iptables SNAT与伪装

    Source NAT(SNAT)的主要應用,是让同一內部網路上的多部主机,可共用同一条Internet实体连線.直接与Internet相连的闸道器,可使用SNAT(搭配连線追蹤)来来改写內部网络与In ...