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 本页内容包括: 简 ...
随机推荐
- OSGi教程:Resource API Specification
此教程基于OSGi Core Release 7 OSGi Resource API规范 详细内容上面英文教程有详细解答 下面主要是一些个人见解,若有不当之处,欢迎指出: Resource:就是能够被 ...
- 【JZOJ4743】【NOIP2016提高A组模拟9.2】积木
题目描述 输入 输出 样例输入 3 8 7 6 3 9 4 1 10 5 输出 18 数据范围 样例解释 解法 容易从n<=15得出可以使用状态压缩动态规划. 设f[i][j][k]表示01状态 ...
- 大数据技术之Hive
第1章 Hive入门 1.1 什么是Hive Hive:由Facebook开源用于解决海量结构化日志的数据统计. Hive是基于Hadoop的一个数据仓库工具,可以将结构化的数据文件映射为一张表,并提 ...
- 在APPfuse中配置log4j进行定位
appFuse使用log4j进行日志输出,默认日志级别为warn,输出到串口console. 一下修改为debug级别,输出到串口及文件中: 1.修改(tomcat中的页面根目录)/appfuse/W ...
- pl/sql基础知识—过程快速入门
n 过程 过程用于执行特定的操作,当建立过程时,既可以指定输入参数(in),也可以指定输出参数(out),通过在过程中使用输入参数,可以将数据传递到执行部分:通过使用输出参数可以将执行部分的数据传递 ...
- shell学习(22)- comm
1.预备知识 comm命令可用于比较两个已排序的文件.它可以显示出第一个文件和第二个文件所独有的行以及这两个文件所共有的行.该命令有一些选项可以禁止显示指定的列,以便于执行交集和求差操作. 交集(in ...
- Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第八章:光照
原文:Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第八章:光照 代码工程地址: https://github.com/j ...
- Effective C++: 05实现
26:尽可能延后变量定义式的出现时间 1:只要你定义了一个变量而其类型带有一个构造函数或析构函数,那么当程序的控制流到达这个变量定义式时,你便得承受构造成本:当这个变量离开其作用域时,你便得承受析构成 ...
- phpexcel使用说明2
转自:http://serisboy.iteye.com/blog/1928139 首先到phpexcel官网上下载最新的phpexcel类,下周解压缩一个classes文件夹,里面包含了PHPE ...
- React Native等比放大不丢失图片
9月11号 0.33版本,resizeMode中添加了center, 可以实现一样的功能.不过希望这篇文章还能帮助大家. 之前我们学习了从零学React Native之08Image组件 大家可以发现 ...