C# double 好用的扩展
在很多代码需要使用数学计算,在用到 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) < 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) < 1 and
/// (1 + PositiveDoublePrecision) > 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 "double precision" floating point numbers have a significand precision that utilize 53 bits.
///
/// This number has the following properties:
/// (1 - NegativeMachineEpsilon) < 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 "double precision" floating point numbers have a significand precision that utilize 53 bits.
///
/// This number has the following properties:
/// (1 - PositiveDoublePrecision) < 1 and
/// (1 + PositiveDoublePrecision) > 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
我搭建了自己的博客 https://blog.lindexi.com/ 欢迎大家访问,里面有很多新的博客。只有在我看到博客写成熟之后才会放在csdn或博客园,但是一旦发布了就不再更新
如果在博客看到有任何不懂的,欢迎交流,我搭建了 dotnet 职业技术学院 欢迎大家加入

本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。欢迎转载、使用、重新发布,但务必保留文章署名林德熙(包含链接:http://blog.csdn.net/lindexi_gd ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系。
C# double 好用的扩展的更多相关文章
- 6.Swift协议|扩展|访问权限|异常调试|类型转换|运算函数|ARC|类类型初试化器|值类型初始化器
1. 协议(Protocol):与OC之间唯一不同的是Swift中的协议不管是属性还时方法全部是必须实现的 /** protocol*/ protocol FullNamed { /** 计算属性申明 ...
- 2019-3-1-C#-double-好用的扩展
title author date CreateTime categories C# double 好用的扩展 lindexi 2019-3-1 9:19:5 +0800 2018-05-15 10: ...
- 我的MYSQL学习心得(四) 数据类型
我的MYSQL学习心得(四) 数据类型 我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得(五) 运 ...
- 吉特仓库管理系统-ORM框架的使用
最近在园子里面连续看到几篇关于ORM的文章,其中有两个印象比较深刻<<SqliteSugar>>,另外一篇文章是<<我的开发框架之ORM框架>>, 第一 ...
- Matlab中的数据类型
Matlab中有15种基本数据类型,主要是整型.浮点.逻辑.字符.日期和时间.结构数组.单元格数组以及函数句柄等. 1.整型:(int8:uint8:int16:uint16:int3 ...
- [精校版]The Swift Programming Language
通常来说,编程语言教程中的第一个程序应该在屏幕上打印“Hello, world”.在 Swift 中,可以用一行代码实现: println("hello, world") 如 ...
- 李洪强iOS开发之-Swift_00_介绍
SWIFT (计算机编程语言) Swift,苹果于2014年WWDC(苹果开发者大会)发布的新开发语言,可与Objective-C*共同运行于Mac OS和iOS平台,用于搭建基于苹果平台的应用程序. ...
- Matlab基本数据类型
本文转载自:http://hi.baidu.com/xmf6227/blog/item/97ca2ddf98f1b61f495403cb.html Matlab中有15种基本数据类型,主要是整型.浮点 ...
- Swift 初见
http://numbbbbb.gitbooks.io/-the-swift-programming-language-/chapter1/02_a_swift_tour.html 本页内容包括: 简 ...
随机推荐
- kubernetes1.5新特性跟踪(续)
Kubernetes发布历史回顾 Kubernetes 1.0 - 2015年7月发布 Kubernetes 1.1 - 2015年11月发布 Kubernetes 1.2 - 2016年3月发布 K ...
- 成功的背后!(送给所有IT人)
希望自己迷茫的时候,看到能够惊醒 来自CSDN第3名的博主(http://blog.csdn.net/phphot/article/details/2187505) 成功的背后,有着许多不为人知的故事 ...
- k8s 超详细总结,面试必问
一个目标:容器操作:两地三中心:四层服务发现:五种Pod共享资源:六个CNI常用插件:七层负载均衡:八种隔离维度:九个网络模型原则:十类IP地址:百级产品线:千级物理机:万级容器:相如无亿,K8s有亿 ...
- Directx11教程(66) D3D11屏幕文本输出(1)
原文:Directx11教程(66) D3D11屏幕文本输出(1) 在D3D10中,通过ID3DX10Font接口对象,我们可以方便的在屏幕上输出文字信息,一个DrawText函数就能解决所 ...
- oracle-ORA-01567错误
删除日志4时将在线索1中保留少于两个日志文件
- 初探postman
第一种:安装postman 扩展程序 第二种:本地 安装postman 登陆进来postman的界面 发送第一个postman请求 将请求保存到集合 未完,待续...
- JAVA高级--异常处理概念和异常处理机制
什么是异常 程序运行的过程中发生的一些不正常事件 异常分类 Throwable Error 错误 Exception IOException RuntimeException ...
- AtCoder Regular Contest 085 C HSI【概率论】
AtCoder Regular Contest 085 C HSI 没学概率论还不怎么看得懂,虽然感觉不难,其实明明可以猜出来的..... 参考博客:https://www.cnblogs.com/g ...
- .net 数据表格显示控件
版权声明:本文为博主原创文章.未经博主同意不得转载. https://blog.csdn.net/chenjinge7/article/details/30470609 1. GridView 控件 ...
- Idea创建maven项目 标签: idea 2016-12-28 21:51 605人阅读 评论(27) 收藏
很久之前就听说了idea,界面也的确比eclipse好看,不过一直没有机会使用,这两天试用了一下,代码提示方面的确很好用,不过使用习惯跟eclipse还是有一些差距的,下面介绍一下如何用idea创建一 ...