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

在很多代码需要使用数学计算,在用到 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

2019-3-1-C#-double-好用的扩展的更多相关文章

  1. C# double 好用的扩展

    在很多代码需要使用数学计算,在用到 double 很难直接判断一个值是 0 或者 1 ,判断两个值相等. 本文提供一个数学扩展,让大家可以简单使用到 double 判断 在开始看本文之前,希望大家是知 ...

  2. [翻译] 使用 Visual Studio 2019 来提高每个开发人员的工作效率

    [翻译] 使用 Visual Studio 2019 来提高每个开发人员的工作效率 原文: Making every developer more productive with Visual Stu ...

  3. POJ - 2976 Dropping tests && 0/1 分数规划

    POJ - 2976 Dropping tests 你有 \(n\) 次考试成绩, 定义考试平均成绩为 \[\frac{\sum_{i = 1}^{n} a_{i}}{\sum_{i = 1}^{n} ...

  4. SQL common keywords examples and tricks

    Case Sensitive Check 1. Return names contain upper case Select id, name from A where name<>low ...

  5. C++【stack/queue】用法和例子

    Stack的常用基本操作: s.push() // 压栈 s.emplace() // 插入,相当于push(目前掌握的唯一区别是emplace可以自行调用构造函数,push不行) s.empty() ...

  6. 剖析虚幻渲染体系(15)- XR专题

    目录 15.1 本篇概述 15.1.1 本篇内容 15.1.2 XR概念 15.1.2.1 VR 15.1.2.2 AR 15.1.2.3 MR 15.1.2.4 XR 15.1.3 XR综述 15. ...

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

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

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

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

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

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

  10. Matlab中的数据类型

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

随机推荐

  1. mysql锁机制之表锁(三)

    顾名思义,表锁就是一锁锁一整张表,在表被锁定期间,其他事务不能对该表进行操作,必须等当前表的锁被释放后才能进行操作.表锁响应的是非索引字段,即全表扫描,全表扫描时锁定整张表,sql语句可以通过执行计划 ...

  2. 2019-6-23-开源项目使用-appveyor-自动构建

    title author date CreateTime categories 开源项目使用 appveyor 自动构建 lindexi 2019-06-23 11:47:40 +0800 2019- ...

  3. 纯CSS3打造圆形菜单

    原理是使用相对定位和绝对定位确定圆形菜单位置. 使用伪类选择器E:hover确定悬浮时候的效果,动画效果用CSS3的transition属性. 大概代码如下. html: <div id=&qu ...

  4. SDUT-2088_数据结构实验之栈与队列十一:refresh的停车场

    数据结构实验之栈与队列十一:refresh的停车场 Time Limit: 1000 ms Memory Limit: 65536 KiB Problem Description refresh最近发 ...

  5. Java练习 SDUT-1149_计算题

    计算题 Time Limit: 1000 ms Memory Limit: 65536 KiB Problem Description 一个简单的计算,你需要计算f(m,n),其定义如下: 当m=1时 ...

  6. 在SAE上使用Flask插件

    因为我之前学习的时候使用的是虚拟环境,下载的所有需要用到的插件都在flask这个文件夹里面,SAE上Flask的版本和我本地用的版本对不上,导致有时候import都不对,于是我就把本地的环境直接放到S ...

  7. ImportError: No module named tensorflow.compat.v1 忽略已经安装的某个包版本 忽略已安装版本

    ImportError: No module named tensorflow.compat.v1 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声 ...

  8. oracle函数 SESSIONTIMEZONE

    [功能]:返回会话时区 [参数]:没有参数,没有括号 [返回]:字符型 [示例]select dbtimezone,SESSIONTIMEZONE from dual; 返回:+00:00   +08 ...

  9. ImportError: No module named libqt_gui_cpp_shiboken

    在使用 rosrun rqt_publisher rqt_publisher 调用ROS图形化界面的过程中出现: 而且在使用图像化界面添加/cmd_vel时,无法添加,命令窗口显示“段错误”. 在网上 ...

  10. Python基础:26模块

    一:模块和文件 1:模块是逻辑上组织 Python 代码的方法,文件是物理层上组织模块的方法.因此,一个文件被看作是一个独立模块,一个模块也可以被看作是一个文件. 模块的文件名就是模块的名字加上扩展名 ...