Can a near-zero floating value cause a divide-by-zero error?

16,776

Solution 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.

Solution 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 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.

Solution 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

Solution 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.

Solution 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.

Share:
16,776
neuviemeporte
Author by

neuviemeporte

My dream job is to be an MS-DOS programmer in the 80s.

Updated on June 06, 2022

Comments

  • neuviemeporte
    neuviemeporte almost 2 years

    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?

  • Admin
    Admin over 11 years
    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?
  • Reed Copsey
    Reed Copsey over 11 years
    @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...
  • Admin
    Admin over 11 years
    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.
  • Reed Copsey
    Reed Copsey over 11 years
    @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: msdn.microsoft.com/en-us/library/windows/desktop/…
  • Reed Copsey
    Reed Copsey over 11 years
    @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.
  • Admin
    Admin over 11 years
    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.
  • Reed Copsey
    Reed Copsey over 11 years
    @delnan My current answer is very explicit on that.
  • Admin
    Admin over 11 years
  • neuviemeporte
    neuviemeporte over 11 years
    Is it possible to test if a value is infinity?
  • Pete Becker
    Pete Becker over 11 years
    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.
  • R.. GitHub STOP HELPING ICE
    R.. GitHub STOP HELPING ICE over 11 years
    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
    R.. GitHub STOP HELPING ICE over 11 years
    z!=z is false for infinities. It's only true for nans.
  • Sergey Kalinichenko
    Sergey Kalinichenko over 11 years
    @neuviemeporte Yes - take a look at the update to the answer.
  • Sergey Kalinichenko
    Sergey Kalinichenko over 11 years
    @R.. Thanks, I went to check the trick, and it didn't work, so I went for a lengthier way of checking it.
  • R.. GitHub STOP HELPING ICE
    R.. GitHub STOP HELPING ICE over 11 years
    If you don't care about raising spurious exceptions, you can also check for infinities with if (!(1/x))
  • halex
    halex over 11 years
    @neuviemeporte Since C99 in math.h the macro int isfinite(float x) is defined, that returns a non-zero result only when x is finite. See pubs.opengroup.org/onlinepubs/009604499/functions/isfinite.h‌​tml
  • Eric Postpischil
    Eric Postpischil over 11 years
    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.
  • Pete Becker
    Pete Becker over 11 years
    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.
  • R.. GitHub STOP HELPING ICE
    R.. GitHub STOP HELPING ICE over 11 years
    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.
  • Eric Postpischil
    Eric Postpischil over 11 years
    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).
  • R.. GitHub STOP HELPING ICE
    R.. GitHub STOP HELPING ICE over 11 years
    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
    R.. GitHub STOP HELPING ICE over 11 years
    Per 5.2.4.2.2, The accuracy of the floating-point operations (+, -, *, /) and of the library functions in <math.h> and <complex.h> 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 <stdio.h>, <stdlib.h>, and <wchar.h>. The implementation may state that the accuracy is unknown.
  • Eric Postpischil
    Eric Postpischil over 11 years
    @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
    Eric Postpischil over 11 years
    @R..: Does the C compiler you use most frequently define __STDC_IEC_559__?
  • R.. GitHub STOP HELPING ICE
    R.. GitHub STOP HELPING ICE over 11 years
    @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.
  • neuviemeporte
    neuviemeporte over 11 years
    Is an overflow exception also a no-op?
  • Eric Postpischil
    Eric Postpischil over 11 years
    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.
  • R.. GitHub STOP HELPING ICE
    R.. GitHub STOP HELPING ICE over 11 years
    @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
    R.. GitHub STOP HELPING ICE over 11 years
    @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.
  • Eric Postpischil
    Eric Postpischil over 11 years
    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.
  • neuviemeporte
    neuviemeporte over 11 years
    @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?
  • R.. GitHub STOP HELPING ICE
    R.. GitHub STOP HELPING ICE over 11 years
    @neuviemeporte: No; that would be implementation-specific anyway. As I already said, <fenv.h> has the interfaces for getting at the floating point exception flags if you want to use them.
  • Ruslan
    Ruslan over 8 years
    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.
  • Ant6n
    Ant6n about 8 years
    The question is whether near-zero values can cause a divide-by-zero. You nitpick about errors.