66

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?

Jens
  • 69,818
  • 15
  • 125
  • 179
neuviemeporte
  • 6,310
  • 10
  • 49
  • 78
  • 10
    Don't check for 0 at all. Let it crash hard! – Luchian Grigore Aug 24 '12 at 18:05
  • 24
    Everybody knows you're not supposed to compare floats using a tolerance, but rather in a way that makes sense for what you want. @LuchianGrigore it won't crash. – R. Martinho Fernandes Aug 24 '12 at 18:05
  • 2
    You won't have errors if b isn't exactly 0. But if b is too small, the result may not make sense, it depends on your application logic. – Denys Séguret Aug 24 '12 at 18:07
  • 1
    @LuchianGrigore Only if you don't know what to do in case of 0. But I'm pretty sure that there are cases where it makes sense to treat 0 specially. Also, not all environments make it crash, some just give you a strange value (I'm not sure if it's NaN, one of the infinities, or something else). –  Aug 24 '12 at 18:07
  • 1
    Potentially consider using [Q numbers](http://en.wikipedia.org/wiki/Q_%28number_format%29), if they are suitable to your situation... – 0909EM Aug 24 '12 at 18:08
  • 1
    The well defined float value `infinity` is the result of 1/0.0f so I would not explicit check for 0.0f as you would in case of integers, where dividing by 0 is an error. – halex Aug 24 '12 at 18:16
  • @R.MartinhoFernandes: care to elaborate/provide an example? – neuviemeporte Aug 24 '12 at 21:15
  • No, it cannot produce a division by zero error. It can, however, result in infinity or cause overflow. – Thomas Aug 25 '12 at 00:41
  • 1
    @LuchianGrigore: FYI, floating-point division by zero will NOT crash at all but will instead silently produce INF. And a couple hours of happy debugging before you find out why you're getting funny values. I'd rather it crashed hard. – Violet Giraffe Jul 30 '16 at 17:48

5 Answers5

70

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.

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
  • 28
    Let me underscore: a floating-point exception is **not** a C++ exception. You can't put a try-catch block around it. You won't see it unless you go gunning for it, and that takes a bit of expertise. As @R.. said, it's a no-op unless you know how to find it. And you don't want to do that unless you're an expert. – Pete Becker Aug 24 '12 at 18:39
  • 1
    Thanks Pete for emphasizing that point. Unless you're using `fenv.h` (most people haven't even *heard of* it, much less used it), floating point exceptions are irrelevant/no-ops. – R.. GitHub STOP HELPING ICE Aug 24 '12 at 18:41
  • 5
    The C standard does not define the default floating-point environment unless an implementation adopts Annex F. Without Annex F, traps may be enabled by default. Using high-performance options when compiling may enable modes that do not conform to the C standard or with IEEE 754. A blanket statement that division by zero is not an error is unwarranted. – Eric Postpischil Aug 24 '12 at 19:07
  • 4
    Without Annex F, there are basically no requirements on behavior of floating point. `2.0+2.0==5.0` can be true. It's meaningless to talk about floating point in C without assuming Annex F. – R.. GitHub STOP HELPING ICE Aug 24 '12 at 19:43
  • If your assertion about Annex F were correct, then your answer should say, yes, dividing by zero can cause a trap, and so can dividing by a subnormal number. In fact, the C standard contains many specifications about float and double before it gets to Annex F, and people have made productive use of them for many years. A C implementation in which 2.+2.==5. would not be regarded as conforming, with or without Annex F. But a C implementation in which dividing by 0. trapped would be regarded as conforming (in the absence of other issues). – Eric Postpischil Aug 24 '12 at 20:20
  • 3
    Can you please cite the part of the C standard that implies `2.0+2.0!=5.0`? I am not aware of any such requirement. – R.. GitHub STOP HELPING ICE Aug 24 '12 at 20:38
  • 1
    Per 5.2.4.2.2, *The accuracy of the floating-point operations (+, -, *, /) and of the library functions in and that return floating-point results is implementation- defined, as is the accuracy of the conversion between floating-point internal representations and string representations performed by the library functions in , , and . The implementation may state that the accuracy is unknown.* – R.. GitHub STOP HELPING ICE Aug 24 '12 at 20:42
  • @R..: First, does the C compiler you use most frequently define \_\_STDC\_IEC\_559\_\_? Second, even if you are correct that Annex F is needed in order to provide assertions about floating-point behavior, how do you justify assuming that Annex F is adopted? Do we just get to assume implementation-defined portions of the standard when answering questions now? The fact is, as the question was asked, the C standard does not guarantee that division by zero does not trap. – Eric Postpischil Aug 24 '12 at 20:50
  • @R..: Does the C compiler you use most frequently define \_\_STDC\_IEC\_559\_\_? – Eric Postpischil Aug 24 '12 at 21:05
  • @Eric: No, gcc does not, in the interest of being conservative in making promises about its own quality. It's not that it does not intend to provide the required semantics, but that it has a responsibility not to define that macro unless it's 100% sure it's delivering the required semantics. Historically gcc has a lot of floating point conformance bugs. Most of them have been fixed now. – R.. GitHub STOP HELPING ICE Aug 24 '12 at 21:07
  • Is an overflow exception also a no-op? – neuviemeporte Aug 24 '12 at 21:13
  • There are any number of questions on Stack Overflow which are languages-lawyered to understand cases that *might* conform to the C standard but that are rare in modern implementations. And here we have a case which is *known* to be counter to fact; common compilers do *not* adopt Annex F. Yet that is the main part of this answer. This answer prominently states a claim based on an assumption that is *known to be false* in common implementations, and it minimizes the true answer, which is that you must check your C implementation for this. – Eric Postpischil Aug 24 '12 at 21:23
  • 1
    @Eric: Now you're really misrepresenting things; the fact that common compilers do not publish a claim of 100% conformance to Annex F (note: GCC does not even conform 100% to C99 without Annex F) does not imply that implementations which ignore IEEE semantics are common or even relevant. The real-world situation is that you do have some implementations where denormals are flushed to zero (often as a non-default option for performance, in the same spirit as `-ffast-math`) and some where floating point exceptions don't even exist (e.g. ARM EABI), but none which trap on divide-by-zero. – R.. GitHub STOP HELPING ICE Aug 24 '12 at 21:44
  • 1
    @neuviemeporte: All floating point exceptions do is set an invisible flag that you can read or clear using the functions in `fenv.h`, and this is only valid after using the `#pragma` to turn on `FENV_ACCESS` (otherwise compiler optimizations might lose exceptions or raise spurious ones). Overflow, like divide-by-zero, is not an error (per IEEE arithmetic). As pointed out by Eric, C makes no guarantees about any of this, so you need to consult your implementation's documentation if you can't assume IEEE. – R.. GitHub STOP HELPING ICE Aug 24 '12 at 21:48
  • A compiler that does not publish a claim of conformance to Annex F by definition does not conform to Annex F, since defining \_\_STDC\_IEC\_559\_\_ is required by Annex F. You simply cannot perform engineering by assuming that things must behave when you do not have a specification to that effect. You surrendered any basis for a “real world” argument by positing a C implementation in which 2.+2.==5. If you use that possibility to make deductions about compiler behavior, then you are compelled not to assume IEEE 754 semantics or a default floating-point environment. – Eric Postpischil Aug 24 '12 at 21:50
  • @R..: By "invisible flag", do you mean EFLAGS? If so, I suppose you could write a small asm block, push eflags, pop dword ptr and shift to get CF? – neuviemeporte Aug 24 '12 at 23:27
  • @neuviemeporte: No; that would be implementation-specific anyway. As I already said, `` has the interfaces for getting at the floating point exception flags if you want to use them. – R.. GitHub STOP HELPING ICE Aug 24 '12 at 23:33
  • The question is whether near-zero values can cause a divide-by-zero. You nitpick about errors. – Ant6n Feb 10 '16 at 16:40
26

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 subnormal can cause the same result as dividing by zero. Flush-underflow mode is not uncommon when vector (SIMD) instructions are used.

Subnormal 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 subnormal 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 subnormal operands to zero. Dividing a number by a subnormal 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.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • 3
    Totally nitpicky point: the 2008 revision of IEEE-754 changed "denormal" to "subnormal". This change in terminology does not change any of the points made above. – Pete Becker Aug 24 '12 at 19:26
  • `requires additional computing time in some processors` In fact, it's not in _some_ processors — it's in **most** of the current processors, including x86 and ARM ones. – Ruslan Aug 11 '15 at 08:36
9

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
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
8

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.

Jens Agby
  • 410
  • 2
  • 9
3

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.

Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
  • 1
    Was that really the question? It's great that 0 is represented exactly, but does that imply no other value can have the same result as divison by zero? –  Aug 24 '12 at 18:09
  • @delnan It's not exactly clear - given teh question "Can a near-zero floating value cause a divide-by-zero error?" - yes, I think this answers it directly ;) I did try to raise other points, though, in my answer... – Reed Copsey Aug 24 '12 at 18:10
  • 1
    I'm also referring to that part of the question. After all, it is conceivable (for uneducated me) that some values extremely close to zero cause the same floating point exception as division by exact zero does. -1 for now. –  Aug 24 '12 at 18:12
  • @delnan Typically a true "divide by zero" is really only divide by zero. I believe this may be platform specific though - for example, on Windows, div/0 will raise EXCEPTION_FLT_DIVIDE_BY_ZERO by I believe div/(nr 0) can raise EXCEPTION_FLT_OVERFLOW on windows - See: http://msdn.microsoft.com/en-us/library/windows/desktop/aa363082(v=vs.85).aspx – Reed Copsey Aug 24 '12 at 18:15
  • @delnan Added (after looking up) the IEEE 754 floating point rules - that's probably the closest thing to a "standard" for this that can be used. – Reed Copsey Aug 24 '12 at 18:22
  • I'm sorry, I think you still miss the question as I understood it: "Is there a value other than 0.0 which causes **the divide by zero** floating point exception?" You just told us that there are other exceptions, which is definitely useful (removed downvote) but not the whole story. –  Aug 24 '12 at 18:22
  • @delnan My current answer is very explicit on that. – Reed Copsey Aug 24 '12 at 18:29
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/15775/discussion-between-delnan-and-reed-copsey) –  Aug 24 '12 at 18:29