检查浮点值是否等于0是安全的?

I know you can't rely on equality between double or decimal type values normally, but I'm wondering if 0 is a special case.

While I can understand imprecisions between 0.00000000000001 and 0.00000000000002, 0 itself seems pretty hard to mess up since it's just nothing. If you're imprecise on nothing, it's not nothing anymore.

But I don't know much about this topic so it's not for me to say.

double x = 0.0;
return (x == 0.0) ? true : false;

Will that always return true?

#0

It is safe to expect that the comparison will return true if and only if the double variable has a value of exactly 0.0 (which in your original code snippet is, of course, the case). This is consistent with the semantics of the == operator. a == b means "a is equal to b".

It is not safe (because it is not correct) to expect that the result of some calculation will be zero in double (or more generally, floating point) arithmetics whenever the result of the same calculation in pure Mathematics is zero. This is because when calculations come into the ground, floating point precision error appears - a concept which does not exist in Real number arithmetics in Mathematics.

#1

If you need to do a lot of "equality" comparisons it might be a good idea to write a little helper function or extension method in .NET 3.5 for comparing:

public static bool AlmostEquals(this double double1, double double2, double precision)
{
    return (Math.Abs(double1 - double2) <= precision);
}

This could be used the following way:

double d1 = 10.0 * .1;
bool equals = d1.AlmostEquals(0.0, 0.0000001);

#2

For your simple sample, that test is okay. But what about this:

bool b = ( 10.0 * .1 - 1.0 == 0.0 );

Remember that .1 is a repeating decimal in binary and can't be represented exactly. Then compare that to this code:

double d1 = 10.0 * .1; // make sure the compiler hasn't optimized the .1 issue away
bool b = ( d1 - 1.0 == 0.0 );

I'll leave you to run a test to see the actual results: you're more likely to remember it that way.

#3

From the MSDN entry for Double.Equals:

Precision in Comparisons

The Equals method should be used with caution, because two apparently equivalent values can be unequal due to the differing precision of the two values. The following example reports that the Double value .3333 and the Double returned by dividing 1 by 3 are unequal.

...

Rather than comparing for equality, one recommended technique involves defining an acceptable margin of difference between two values (such as .01% of one of the values). If the absolute value of the difference between the two values is less than or equal to that margin, the difference is likely to be due to differences in precision and, therefore, the values are likely to be equal. The following example uses this technique to compare .33333 and 1/3, the two Double values that the previous code example found to be unequal.

Also, see Double.Epsilon.

#4

The problem comes when you are comparing different types of floating point value implementation e.g. comparing float with double. But with same type, it shouldn't be a problem.

float f = 0.1F;
bool b1 = (f == 0.1); //returns false
bool b2 = (f == 0.1F); //returns true

The problem is, programmer sometimes forgets that implicit type cast (double to float) is happening for the comparison and the it results into a bug.

#5

If the number was directly assigned to the float or double then it is safe to test against zero or any whole number that can be represented in 53 bits for a double or 24 bits for a float.

Or to put it another way you can always assign and integer value to a double and then compare the double back to the same integer and be guaranteed it will be equal.

You can also start out by assigning a whole number and have simple comparisons continue to work by sticking to adding, subtracting or multiplying by whole numbers (assuming the result is less than 24 bits for a float abd 53 bits for a double). So you can treat floats and doubles as integers under certain controlled conditions.

#6

No, it is not OK. So-called denormalized values (subnormal), when compared equal to 0.0, would compare as false (non-zero), but when used in an equation would be normalized (become 0.0). Thus, using this as a mechanism to avoid a divide-by-zero is not safe. Instead, add 1.0 and compare to 1.0. This will ensure that all subnormals are treated as zero.

推荐文章

Developing C++ with Visual Studio Code

Developing C++ with Visual Studio Code

推荐文章

When Math Fails You

When Math Fails You

推荐文章

动态分析VBA文件时出现问题

动态分析VBA文件时出现问题

推荐文章

Aerospike C客户端手册———目录

Aerospike C客户端手册———目录

推荐文章

如何忽略已提交的文件?

如何忽略已提交的文件?

推荐文章

jQuery弹出气泡/工具提示

jQuery弹出气泡/工具提示

推荐文章

Aerospike C客户端手册———简介

Aerospike C客户端手册———简介

推荐文章

一种中等复杂度的TDD方法

一种中等复杂度的TDD方法

推荐文章

在.NET中序列化对象时是否忽略所有xsi和xsd命名空间?

在.NET中序列化对象时是否忽略所有xsi和xsd命名空间?

推荐文章

用什么替换这个java代码?

用什么替换这个java代码?

推荐文章

Why You Shouldn't Use A Web Framework

Why You Shouldn't Use A Web Framework

推荐文章

5版本的MySQL数据库的最大大小是多少?

5版本的MySQL数据库的最大大小是多少?

推荐文章

如何将DOM元素转换为jQuery元素?

如何将DOM元素转换为jQuery元素?

推荐文章

To Queue or not to Queue?

To Queue or not to Queue?

推荐文章

如何将对象/结构转换为arraylist?

如何将对象/结构转换为arraylist?

推荐文章

OCaml对象中的递归函数

OCaml对象中的递归函数