3

According to C++ Standard (5/5) dividing by zero is undefined behavior. Now consider this code (lots of useless statements are there to prevent the compiler from optimizing code out):

int main()
{
    char buffer[1] = {};
    int len = strlen( buffer );
    if( len / 0 ) {
        rand();
    }
}

Visual C++ compiles the if-statement like this:

sub         eax,edx 
cdq 
xor         ecx,ecx 
idiv        eax,ecx 
test        eax,eax 
je          wmain+2Ah (40102Ah) 
call        rand

Clearly the compiler sees that the code is to divide by zero - it uses xor x,x pattern to zero out ecx which then serves the second operand in integer division. This code will definitely trigger an "integer division by zero" error at runtime.

IMO such cases (when the compiler knows that the code will divide by zero at all times) are worth a compile-time error - the Standard doesn't prohibit that. That would help diagnose such cases at compile time instead of at runtime.

However I talked to several other developers and they seem to disagree - their objection is "what if the author wanted to divide by zero to... emm... test error handling?"

Intentionally dividing by zero without compiler awareness is not that hard - using __declspec(noinline) Visual C++ specific function decorator:

__declspec(noinline)
void divide( int what, int byWhat )
{
    if( what/byWhat ) {
       rand();
    }
}

void divideByZero()
{
    divide( 0, 0 );
}

which is much more readable and maintainable. One can use that function when he "needs to test error handling" and have a nice compile-time error in all other cases.

Am I missing something? Is it necessary to allow emission of code that the compiler knows divides by zero?

Aziz Shaikh
  • 16,245
  • 11
  • 62
  • 79
sharptooth
  • 167,383
  • 100
  • 513
  • 979
  • I assume your question only applies to fundamental types. Am I right? – R. Martinho Fernandes Oct 17 '11 at 07:02
  • @R. Martinho Fernandes: Yes, let's consider it limited to fundamental types. – sharptooth Oct 17 '11 at 07:12
  • The problem I see is that only a minority of errors would actually be caught at compile time the rest are really run time errors. I don't think there is another class of error that has this kind of duality. Which is a bit strange. – Martin York Oct 17 '11 at 07:16
  • Also I would prefer to have a warning so it can be turned on/off as required for backwards comparability/testing etc – Martin York Oct 17 '11 at 07:17
  • Are you considering floating point divide by zero as well? If so, it can be very useful. – Drew Hall Oct 17 '11 at 07:18
  • I guess what caught me out was the inclusion of assembly in a question about syntax. – David Heffernan Oct 17 '11 at 07:21
  • In fact, would it even be valid to emit compile error for divide by zero? Isn't the undefined behaviour the runtime behaviour? – David Heffernan Oct 17 '11 at 07:24
  • @DavidHeffernan: no, it is just undefined. It could even produce a time warp and generate a compile error in the code you compiled *yesterday*. It is perfectly legal for the compiler to produce an error on undefined behavior. "Undefined" means exactly what it says on the box. There is no definition of what it should or should not do, and so, there is no rule that whatever it does should be confined to run-time – jalf Oct 17 '11 at 07:26
  • 1
    I was unconvinced a compiler was allowed to reject a well formed program just because of the existence of undefined behavior, but apparently it can. 1.3, under the definition of undefined behavior: "Permissible undefined behavior ranges from . . . to terminating a translation ... (with the issuance of a diagnostic message)." Its in a note rather than a proper rule, but it is good enough for me. However, I still would not want to see compilers be overly aggressive with this particular option. – Dennis Zickefoose Oct 17 '11 at 07:50

5 Answers5

4

There is probably code out there which has accidental division by zero in functions which are never called (e.g. because of some platform-specific macro expansion), and these would no longer compile with your compiler, making your compiler less useful.

Also, most division by zero errors that I've seen in real code are input-dependent, or at least are not really amenable to static analysis. Maybe it's not worth the effort of performing the check.

Simon Nickerson
  • 42,159
  • 20
  • 102
  • 127
  • Okay, but those functions are then broken, aren't they? – sharptooth Oct 17 '11 at 07:13
  • The last line seems most important to me. If 99% of the time the compiler can't know it is going to happen, how much are you really gaining going after the remaining cases? – Dennis Zickefoose Oct 17 '11 at 07:23
  • I think the last paragraph is the answer. Compilers are slow enough as it is. Is it worth checking for one more error when it will almost never help the user? Nearly all divisions by zero are input dependent, so how much do we gain by checking for it at compile time? – jalf Oct 17 '11 at 07:25
  • @sharptooth: Maybe, maybe not. What if the reason they are never called is *also* the reason they contain the division by zero? Its rather like the warning about missing a return statement... if it only happens when `i < 0`, but `i < 0` means some function I call is going to throw an exception I have no intention of catching, then the function is perfectly well formed and a hard error would be needlessly aggressive.. – Dennis Zickefoose Oct 17 '11 at 07:39
4

Dividing by 0 is undefined behavior because it might trigger, on certain platforms, a hardware exception. We could all wish for a better behaved hardware, but since nobody ever saw fit to have integers with -INF/+INF and NaN values, it's quite pointeless.

Now, because it's undefined behavior, interesting things may happen. I encourage you to read Chris Lattner's articles on undefined behavior and optimizations, I'll just give a quick example here:

int foo(char* buf, int i) {
  if (5 / i == 3) {
    return 1;
  }

  if (buf != buf + i) {
    return 2;
  }

  return 0;
}

Because i is used as a divisor, then it is not 0. Therefore, the second if is trivially true and can be optimized away.

In the face of such transformations, anyone hoping for a sane behavior of a division by 0... will be harshly disappointed.

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • I'm afraid I can't find the assert in this snippet. – sharptooth Oct 17 '11 at 07:17
  • Well, the second `if` statement is trivially true and can be optimized away. I'm guessing that's what he meant. – jalf Oct 17 '11 at 07:21
  • @sharptooth: I think he's referring to the "if (buf != buf + i)" test. Because i must not be zero (or we're in UB-land) that test is trivially true and it and everything following it can be legally replaced with "return 2". – Drew Hall Oct 17 '11 at 07:21
  • @jalf: yes, sorry, I changed the example :x – Matthieu M. Oct 17 '11 at 07:29
  • 2
    Nice example. Even better is that the whole function can simply be replaced with `return 2' since there is no integer value for `i` where `5/i == 3`. – edA-qa mort-ora-y Oct 17 '11 at 08:05
1

In the case of integral types (int, short, long, etc.) I can't think of any uses for intentional divide by zero offhand.

However, for floating point types on IEEE-compliant hardware, explicit divide by zero is tremendously useful. You can use it to produce positive & negative infinity (+/- 1/0), and not a number (NaN, 0/0) values, which can be quite helpful.

In the case of sorting algorithms, you can use the infinities as initial values representing greater or less than all possible values.

For data analysis purposes, you can use NaNs to indicate missing or invalid data, which can then be handled gracefully. Matlab, for example, uses explicit NaN values to suppress missing data in plots, etc.

Although you can access these values through macros and std::numeric_limits (in C++), it is useful to be able to create them on your own (and allows you to avoid lots of "special case" code). It also allows implementors of the standard library to avoid resorting to hackery (such as manual assembly of the correct FP bit sequence) to provide these values.

Drew Hall
  • 28,429
  • 12
  • 61
  • 81
0

If the compiler detects a division-by-0, there is absolutely nothing wrong with a compiler error. The developers you talked to are wrong - you could apply that logic to every single compile error. There is no point in ever dividing by 0.

Ayjay
  • 3,413
  • 15
  • 20
0

Detecting divisions by zero at compile-time is the sort of thing that you'd want to have be a compiler warning. That's definitely a nice idea.

I don't keep no company with Microsoft Visual C++, but G++ 4.2.1 does do such checking. Try compiling:

#include <iostream>

int main() {
    int x = 1;
    int y = x / 0;
    std::cout << y;
    return 0;
}

And it will tell you:

test.cpp: In function ‘int main()’:
test.cpp:5: warning: division by zero in ‘x / 0’

But considering it an error is a slippery slope that the savvy know not to spend too much of their spare time climbing. Consider why G++ doesn't have anything to say when I write:

int main() {
    while (true) {
    }
    return 0;
}

Do you think it should compile that, or give an error? Should it always give a warning? If you think it must intervene on all such cases, I eagerly await your copy of the compiler you've written that only compiles programs that guarantee successful termination! :-)