在很多代码需要使用数学计算,在用到 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. golang中包的初始化

    1.当一个go源程序被初始化时,首先去初始化所依赖的其他包,然后初始化该go源码文件的全局变量的初始化和执行初始化函数,其中该包所有的全局变量初始化在前,该包的初始化函数int在后.当所有包的初始化函 ...

  2. Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 全书总结

    原文:Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 全书总结 本系列文章中可能有很多翻译有问题或者错误的地方:并且有些章节 ...

  3. 洛谷P1879 玉米田

    题目描述 农场主John新买了一块长方形的新牧场,这块牧场被划分成M行N列(1 ≤ M ≤ 12; 1 ≤ N ≤ 12),每一格都是一块正方形的土地.John打算在牧场上的某几格里种上美味的草,供他 ...

  4. python系列之(3)爬取豆瓣图书数据

    上次介绍了beautifulsoup的使用,那就来进行运用下吧.本篇将主要介绍通过爬取豆瓣图书的信息,存储到sqlite数据库进行分析. 1.sqlite SQLite是一个进程内的库,实现了自给自足 ...

  5. 百度网盘直链下载助手(MacOS&Chrome)

    简介 众所周知,通过百度网盘(未开通会员)直接下载文件的速度极慢,通过安装浏览器插件可以极大的提高下载速度. 安装文件 Tampermonkey NeatDownloadManager Extensi ...

  6. Chef 安装

    http://www.tuicool.com/articles/RnAVn2 三个角色: chef server, chef workstation, chef nodes(chef clients) ...

  7. Python基础:11变量作用域和闭包

    一:变量作用域 变量可以是局部域或者全局域.定义在函数内的变量有局部作用域,在一个模块中最高级别的变量有全局作用域. 全局变量的一个特征是除非被删除掉,否则它们的存活到脚本运行结束,且对于所有的函数, ...

  8. Flask学习之四 数据库

    英文博客地址:http://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-iv-database 中文翻译地址:http://ww ...

  9. 17-3 cookie和session

    一 . Cookie 1.cookie 是什么? 保存在浏览器端的键值对! 服务端在返回响应的时候,告诉浏览器保存的键值对!浏览器可以拒绝保存Cookie. 2. 为什么要有cookie? HTTP请 ...

  10. Oracle使用——varchar2() 和 char()关联查询 存在空格

    背景 表dbcontinfo 字段loanid,类型为varchar2(60) 表dbloanbal 字段loanid,类型为char(60) loanid字段实际长度为24位 问题 两张表dbloa ...