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. Directx教程(22) 简单的光照模型(1)

    原文:Directx教程(22) 简单的光照模型(1)      在前面的教程中,我们在顶点属性中直接给顶点赋颜色,这样生成的三维物体缺乏真实感,如下图中两个立方体,左边的是通过光照生成物体表面颜色的 ...

  2. 小爬爬1:开篇&&简单介绍启动

    1.第一阶段的内容 2.学习的方法? 思考,总结,重复 3.长大了意味着什么?家庭的责任,真的很重 4.数据分析&&数据清洗 numpy&&pandas&&am ...

  3. thinkphp5.0中英文切换

    首先来看下它的配置: // 是否开启多语言 'lang_switch_on' => true, //语音列表 'lang_list' => ['zh-cn','en-us'], // 获取 ...

  4. vue vscode属性标签不换行

    "vetur.format.defaultFormatterOptions": { "js-beautify-html": { "wrap_attri ...

  5. 一 linux安装python3

    参考 https://www.cnblogs.com/pyyu/p/7402145.html?tdsourcetag=s_pcqq_aiomsg 1 下载 网址:https://www.python. ...

  6. Xcode编译报错:< Apple Mach-O Linker Warning > clang: error: no such file or directory: 'xxxx'

    Xcode编译报错概述: clang: error: no such file or directory: 'CoreGraphics' 一般原因是链接库内容导入丢失,这种的排查下target - B ...

  7. SDUT-2116_数据结构实验之链表一:顺序建立链表

    数据结构实验之链表一:顺序建立链表 Time Limit: 1000 ms Memory Limit: 65536 KiB Problem Description 输入N个整数,按照输入的顺序建立单链 ...

  8. ArcGIS中的连接和关联表

    大多数数据库的设计指导方针都倾向于将数据库组织成多个表 - 每个表关注一个特定的主题 - 而非一个包含所有必要字段的大型表.设置多个表可以避免数据库中的信息发生重复,因为只会将信息在一个表中存储一次. ...

  9. oracle函数 INSTRB(C1,C2[,I[,J]])

    [功能]在一个字符串中搜索指定的字符,返回发现指定的字符的位置; [说明]多字节符(汉字.全角符等),按2个字符计算 [参数] C1    被搜索的字符串 C2    希望搜索的字符串 I     搜 ...

  10. OpenStack Nova启动实例流程

    1.概述 启动一个新的实例,会涉及到OpenStack Nova中的多个组件: API服务器,接收用户端的请求,并且将其传递给云控制器. 云控制器,处理计算节点.网络控制器.API服务器和调度器之前的 ...