问题
Everybody knows you're not supposed to compare floats directly, but rather using a tolerance:
float a,b;
float epsilon = 1e-6f;
bool equal = (fabs(a-b) < epsilon);
I was wondering if the same applies to comparing a value to zero before using it in division.
float a, b;
if (a != 0.0f) b = 1/a; // oops?
Do I also need to compare with epsilon in this case?
回答1:
Floating point division by zero is not an error. It raises a floating point exception (which is a no-op unless you're actively checking them) on implementations that support floating point exceptions, and has well-defined result: either positive or negative infinity (if the numerator is nonzero), or NAN (if the numerator is zero).
It's also possible to get infinity (and an overflow exception) as the result when the denominator is nonzero but very close to zero (e.g. subnormal), but again this is not an error. It's just how floating point works.
Edit: Note that, as Eric has pointed out in the comments, this answer assumes the requirements of Annex F, an optional part of the C standard detailing floating point behavior and aligning it with the IEEE standard for floating point. In the absence of IEEE arithmetic, C does not define floating point division by zero (and in fact, the results of all floating point operations are implementation-defined and may be defined as complete nonsense and still conform to the C standard), so if you're dealing with an outlandish C implementation that does not honor IEEE floating point, you'll have to consult the documentation for the implementation you're using to answer this question.
回答2:
Yes, dividing by small numbers can cause the same effects as dividing by zero, including traps, in some situations.
Some C implementations (and some other computing environments) may execute in a flush-underflow mode, especially if options for high-performance are used. In this mode, dividing by a denormal can cause the same result as dividing by zero. Flush-underflow mode is not uncommon when vector (SIMD) instructions are used.
Denormal numbers are those with the minimum exponent in the floating-point format which are so small that the implicit bit of the significand is 0 instead of 1. For IEEE 754, single-precision, this is non-zero numbers with magnitude less than 2-126. For double-precision, it is non-zero numbers with magnitude less than 2-1022.
Handling denormal numbers correctly (in accordance with IEEE 754) requires additional computing time in some processors. To avoid this delay when it is not needed, processors may have a mode which convert denormal operands to zero. Dividing a number by a denormal operand will then produce the same result as dividing by zero, even if the usual result would be finite.
As noted in other answers, dividing by zero is not an error in C implementations that adopt Annex F of the C standard. Not all implementations that do. In implementations that do not, you cannot be sure whether floating-point traps are enabled, in particular the trap for the divide-by-zero exception, without additional specifications about your environment.
Depending on your situation, you might also have to guard against other code in your application altering the floating-point environment.
回答3:
To answer the question in the title of your post, dividing by a very small number will not cause a division by zero, but it may cause the result to become an infinity:
double x = 1E-300;
cout << x << endl;
double y = 1E300;
cout << y << endl;
double z = y / x;
cout << z << endl;
cout << (z == std::numeric_limits<double>::infinity()) << endl;
This produces the following output:
1e-300
1e+300
inf
1
回答4:
Only a division by exactly 0.f will raise a division by zero exception.
However, division by a really small number can generate an overflow exception - the result is so large that it no longer can be represented by a float. The division will return infinity.
The float representation of infinity can be used in calculations so there might not be a need to check for it if the rest of your implementation can handle it.
回答5:
Do I also need to compare with epsilon in this case?
You won't ever receive a divide by zero error, as 0.0f
is represented exactly in an IEEE float.
That being said you may still want to use some tolerance - though this depends completely on your application. If the "zero" value is the result of other math, it's possible to get a very small, non-zero number, which may cause an unexpected result after your division. If you want to treat "near zero" numbers as zero, a tolerance would be appropriate. This completely depends on your application and goals, however.
If your compiler is using IEEE 754 standards for exception handling, then divide by zero, as well as a division by a value which is small enough to cause an overflow, would both result in a value of +/- infiniti. This could mean that you could want to include the check for very small numbers (that would cause an overflow on your platform). For example, on Windows, float
and double
both conform to the specifications, which could cause a very small divisor to create +/- infiniti, just like a zero value.
If your compiler/platform is not following IEEE 754 floating point standards, then I believe the results are platform specific.
来源:https://stackoverflow.com/questions/12114498/can-a-near-zero-floating-value-cause-a-divide-by-zero-error