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 本页内容包括: 简 ...
随机推荐
- 如何在liferay 7 mvc-portlet中调用service-builder项目生成的service
不想写了,贴大神帖子 https://web.liferay.com/web/zhao.jin/blog/-/blogs/creating-service-builder-mvc-portlet-in ...
- Codeforces 13C
这题纠结了近半年,一直没有好的思路. 刚开始看这题的时候就是暴力,明显的TLE 后来才知道这题的“一种解”肯定是"原数列中某些数的集合" (很明显这题的最优策略并不唯一) 有原数列 ...
- Spring新特性_泛型依赖注入
泛型依赖注入 package com.tanlei.spring.generic; import org.springframework.beans.factory.annotation.Autowi ...
- mac进行acm(C/C++)编程
在windows下我们往往使用CB(CodeBlocksks)进行acm的编程,不用建立工程,可以直接编译并运行,然后弹出黑框框,sublime也是这样,只要MinGW搞好就行 在ubuntu下我也常 ...
- JS放在body与head中的不同
放在body和head其实差不多的,只不过是文档解析的时间不同.浏览器解析html是从上到下的.如果把javascript放在head里的话,则先被解析,但这时候body还没有解析,所以$(#btn) ...
- iOS开发 底层抛析运行循环—— RunLoop
http://blog.csdn.net/zc639143029/article/details/50012527 一.RunLoop基本概念 概念:程序的运行循环,通俗的来说就是跑圈. 1. 基本作 ...
- 【风马一族_php】NO4_php基础知识
原文来自:http://www.cnblogs.com/sows/p/6017018.html(博客园的)风马一族 侵犯版本,后果自负 回顾 运算符:算术运算符.逻辑运算符.比较运算符.位运算符.赋值 ...
- Balanced Binary Tree 判断平衡二叉树
Given a binary tree, determine if it is height-balanced. For this problem, a height-balanced binary ...
- AtCoder Beginner Contest 084 C - Special Trains
Special Trains Problem Statement A railroad running from west to east in Atcoder Kingdom is now comp ...
- LeetCode109 Convert Sorted List to Binary Search Tree
Given a singly linked list where elements are sorted in ascending order, convert it to a height bala ...