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. day3-转自金角大王

    本节内容 1. 函数基本语法及特性 2. 参数与局部变量 3. 返回值 嵌套函数 4.递归 5.匿名函数 6.函数式编程介绍 7.高阶函数 8.内置函数 温故知新 1. 集合 主要作用: 去重 关系测 ...

  2. Anaconda入门使用指南

    打算学习 Python 来做数据分析的你,是不是在开始时就遇到各种麻烦呢? 到底该装 Python2 呢还是 Python3 ? 为什么安装 Python 时总是出错? 怎么安装工具包呢? 为什么提示 ...

  3. Oracle使用——impdp导入数据时数据表已经存在

    背景 在做数据迁移时,需要将不同地方的dmp文件整合到一个数据库中,在导入时,目标表已经存在,该如何把数据追加进入目标表中 方法介绍 当使用IMPDP完成数据库导入时,如遇到表已存在时,Oracle提 ...

  4. 生成主键ID,唯一键id,分布式ID生成器雪花算法代码实现

    工具类:  package com.ihrm.common.utils; import java.lang.management.ManagementFactory; import java.net. ...

  5. maven 标签: 项目管理软件 2016-09-11 22:29 323人阅读 评论(24) 收藏

    开始接触itoo的java项目之后,也就开始接触maven,搭建环境中有一个步骤是配置maven和jboss,当时知道jboss是用来部署我们的项目的,但是maven就只知道一个更新maven,那么, ...

  6. Effective C++: 03资源管理

    所谓资源,就是一旦用了它,将来必须还给系统.C++中的资源有:内存.文件描述符.互斥锁.数据库连接.网络socket等. 13:以对象管理资源 1:像下面这个函数: void f() { Invest ...

  7. 基于PyTorch的Seq2Seq翻译模型详细注释介绍(一)

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明.本文链接:https://blog.csdn.net/qysh123/article/detai ...

  8. Codeforces 662D International Olympiad【贪心】

    比赛的时候后缀长度在4位以内的时候分类讨论了一下,其实他们完全是一个套路的..并不需要讨论. 然后没有考虑前导0的情况,就wa了.. 题目链接: http://codeforces.com/probl ...

  9. ntelliJ IDEA2017 + tomcat 即改即生效 实现热部署

    1.点击idea中tomcat设置 2.点击deployment查看Deploy at the server startup 中tomcat每次所运行的包是 xxxx:war 还是其他,如果是xxxx ...

  10. laravel重定向到上一个页面怎么带参数返回 withsucess 成功提示信息

    //控制器中 return back()->with('success','操作成功'); //with的参数1是一个session变量名,参数2为该session变量值,在视图直接这样获取 @ ...