将 System.Double 与 '0'(一个数字,int?)进行比较的正确方法

101

我有这个if表达式,

void Foo()
{
    System.Double something = GetSomething();
    if (something == 0) //Comparison of floating point numbers with equality 
                     // operator. Possible loss of precision while rounding value
        {}
}

这个表达式是否等同于

void Foo()
{
    System.Double something = GetSomething();
    if (something < 1)
        {}
}

因为这样的话,当我输入例如0.9的值时,可能会有问题进入if语句。


3
需要进行浮点数的相等性比较操作吗? :) - George Johnston
1
当然不是。在0和1之间有非常多的值。为什么不自己测试一下呢? - Igby Largeman
12
我刚刚写的和 Resharper 一样,是为了展示我的注意力所在。 - radbyx
@Charles:此外,有很多数字小于0。 - Brian
7个回答

131

你需要多接近零的值?如果你执行了很多浮点运算,这些运算在“无限精度”下可能会得出0,但你最终可能得到非常接近0的结果。

通常在这种情况下,你需要提供某种 epsilon,并检查结果是否仅在该 epsilon 范围内:

if (Math.Abs(something) < 0.001)

你应该使用的epsilon值取决于具体应用——它取决于你正在做什么。

当然,如果结果应该恰好为零,那么简单的相等性检查就可以了。


1
实际上,我需要它确切地为零,应该怎么做?我查找了Double.Zero,但没有找到。有一个常数对吧?顺便说一句,谢谢,我现在明白epsilon的部分了 :) - radbyx
26
只需使用 == 0。你已经有了一个字面常量 - 非常恒定 :) - Jon Skeet

36

如果something被分配了一个除something = 0以外的操作结果,那么最好使用:

if(Math.Abs(something) < Double.Epsilon)
{
//do something
}

编辑: 这段代码是错误的。Epsilon是最小的数字,但并不完全为零。当您希望将一个数字与另一个数字进行比较时,您需要考虑可接受的容差是什么。假设您不关心.00001以外的任何东西。那就是您需要使用的数字。该值取决于域。然而,它几乎肯定不会是Double.Epsilon。


2
这并没有解决舍入问题,例如 Math.Abs(0.1f - 0.1d) < double.Epsilon 的结果是 false - Thomas Lulé
6
Double.Epsilon在这种比较中太小了。Double.Epsilon是double类型能表示的最小正数。 - Evgeni Nabokov
3
这句话没有意义,因为它实际上等同于与0进行比较。-1 - Gaspa79
1
目标是在不使用==的情况下与0的概念进行比较。至少在数学上是有意义的。我假设你手头有一个double,并且想要将其与零的概念进行比较,而不使用==。如果由于任何原因(包括四舍五入),您的double与0d不同,则测试将返回false。这种比较似乎对于任何double都是有效的,并且仅当此double小于可以表示的最小数字时才会返回true,这似乎是测试0概念的良好定义,不是吗? - sonatique
4
@MaurGi: 你错了:double d = Math.Sqrt(10100)*2; double a = Math.Sqrt(40400); if(Math.Abs(a - d) < double.Epsilon) { Console.WriteLine("true"); } - sonatique
显示剩余4条评论

31

您的something 是一个 double,并且您已经在该行中正确地识别出了它。

if (something == 0)

我们的左侧(lhs)有一个double,右侧(rhs)有一个int

但是现在似乎你认为 lhs 将会被转换为 int,然后 == 符号将比较两个整数。这不是发生的情况。从 double 转换为 int显式的转换,不能自动进行。

反之发生了。rhs 被转换为 double,然后 == 符号成为两个 doubles 之间的相等测试。这种转换是隐式(自动的)。

一些人认为写成以下形式更好:

if (something == 0.0)
或者
if (something == 0d)

因为这样很明显你在比较两个双精度数。然而,这只是风格和可读性的问题,因为编译器无论如何都会做同样的事情。

在某些情况下,引入“容差”也是相关的,就像Jon Skeet的回答中所说的那样,但是这种容差也应该是一个double类型。当然,如果你愿意,它可以是1.0,但不一定要是[最小严格正]整数。


20

如果您只想抑制警告,请按照以下步骤操作:

if (something.Equals(0.0))
当然,这只是在你知道漂移不是问题时的有效解决方案。我经常这样做来检查我是否即将除以零。

5

老实说,我认为它们不相等。考虑你自己的例子:something = 0.9 或 0.0004。 在第一种情况下,它将是 FALSE,在第二种情况下,它将是 TRUE。当处理这些类型时,我通常为自己定义精度百分比,并在该精度范围内进行比较。 这取决于您的需求。 类似于...

if(((int)(something*100)) == 0) {


//do something
}

希望这能帮到你。

2
某些东西必须完全为零。 - radbyx
所以你很幸运,这种情况下 :) - Tigran

4

以下是一个示例,展示了问题(在LinQPad中准备 - 如果您没有它,只需使用Console.Writeline代替Dump方法):

void Main()
{
    double x = 0.000001 / 0.1;
    double y = 0.001 * 0.01; 

    double res = (x-y);
    res.Dump();
    (res == 0).Dump();
}

理论上,x和y都应该相同且等于:0.00001,但由于缺乏“无限精度”,这些值略有不同。不幸的是,它们的差异足以在通常比较与0时返回false


3

在编程中,当您想要检查双精度值对于两个符号与零的接近程度时,请优先选择此方法。

public bool IsZero(double value, double precision = 1E-6)
{
    return !double.IsNaN(value)
        && !double.IsInfinity(value)
        && Math.Abs(value) < precision;
}

这个问题已经有六个现有答案,其中包括一个得票最高、被接受的答案,获得了一百多个赞同票。你确定你的解决方案还没有被提出过吗?如果不是,为什么你认为你的方法比已经得到社区验证的现有方案更好?在 Stack Overflow 上提供解释总是有用的,但在问题已经得到 OP 和社区满意解决的情况下,这尤其重要。通过解释你的答案与众不同的地方以及何时可能更受欢迎,来帮助读者理解。 - Jeremy Caney
2
该方法考虑了一个值被认为是零的最低阈值。 - Kevin Hayes Anderson
1
可以升级为 bool IsEqual(double value, double compareTo, double precision) - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接