Why no divide by zero error??

I started to re-read Kernighan and Plauger's Elements of Programming Style (terrific book, BTW), and below is the first program they present (modified for the IDE). Why doesn't the nested matrix[][] assignment throw a divided by zero error when i or j is zero?

void setup() {
  int matrix[5][5];
  int i, j;

  Serial.begin(9600);
  
  for (i = 0; i < 5; i++) {
    for (j = 0; j < 5; j++) {
      matrix[i][j] = (j/i) * (i/j);   // Why??
    }
  }

  for (i = 0; i < 5; i++) {
    for (j = 0; j < 5; j++) {
      Serial.print(matrix[i][j]);
      Serial.print(" ");
    }
    Serial.println();
  }  
}

void loop() {

}

My *guess * would be that the divide-by-zero error is compiler generated. Since it's unknown at compile time the values to be used there's no reason to generate an error - and the processor can't generate one on its own.

Serial.println(5/0); does generate the error in question - since the literal divisor '0' is known at compile time.

Awol addressed it here: no DIVIDE BY ZERO error? - Syntax & Programs - Arduino Forum

In essence he says, since the Atmel processors don't have a divide operator, there is software division which doesn't throw error. Even if it did though, where would an exception actually go - there's no O/S to exit to.

Thanks, Doug, WIldbill. Of course, you're right about the error message...not sure what I was thinking. Anyway, I thought I'd investigate why the code does print out the identity matrix correctly. So, I modified the first for loop:

  for (i = 0; i < 5; i++) {
    for (j = 0; j < 5; j++) {
      matrix[i][j] = (j/i) * (i/j);   // Why??
      
      if (j == 0 || i == 0) {
        Serial.print("i = ");
        Serial.print(i);
        Serial.print("   j = ");
        Serial.print(j);  
        Serial.print("i/j = ");
        Serial.println(i/j);
      }
      
    }
  }

Now the code just locks up where it printed the matrix before. I thought that NAN might be generated and the runtime did something with it.

NAN is the result when using float or double.

The result if you change over to 'float':

i = 0.00   j = 0.00 i/j = nan
i = 0.00   j = 1.00 i/j = 0.00
i = 0.00   j = 2.00 i/j = 0.00
i = 0.00   j = 3.00 i/j = 0.00
i = 0.00   j = 4.00 i/j = 0.00
i = 1.00   j = 0.00 i/j = inf
i = 2.00   j = 0.00 i/j = inf
i = 3.00   j = 0.00 i/j = inf
i = 4.00   j = 0.00 i/j = inf
nan nan nan nan nan 
nan 1.00 1.00 1.00 1.00 
nan 1.00 1.00 1.00 1.00 
nan 1.00 1.00 1.00 1.00 
nan 1.00 1.00 1.00 1.00

I ran this on an Uno:

void setup()
{
  int matrix[5][5];
  int i, j;
  Serial.begin(115200);
  for (i = 0; i < 5; i++)
  {
    for (j = 0; j < 5; j++)
    {
      matrix[i][j] = (j / i) * (i / j); // Why??
      if (j == 0 || i == 0)
      {
        Serial.print("i = ");
        Serial.print(i);
        Serial.print("   j = ");
        Serial.print(j);
        Serial.print(" i/j = ");
        Serial.println(i / j);
      }
    }
  }
  for (i = 0; i < 5; i++)
  {
    for (j = 0; j < 5; j++)
    {
      Serial.print(matrix[i][j]);
      Serial.print(" ");
    }
    Serial.println();
  }
}

void loop()
{
}

and got:

i = 0   j = 0 i/j = 4294967295
i = 0   j = 1 i/j = 0
i = 0   j = 2 i/j = 0
i = 0   j = 3 i/j = 0
i = 0   j = 4 i/j = 0
i = 1   j = 0 i/j = 4294967295
i = 2   j = 0 i/j = 4294967295
i = 3   j = 0 i/j = 4294967295
i = 4   j = 0 i/j = 4294967295
1 0 0 0 0 
0 1 0 0 0 
0 0 1 0 0 
0 0 0 1 0 
0 0 0 0 1

aarg: Duh...getting old is no bueno.

John:
Are you seeing the identity matrix in the second nest loop set? It appears the print() defaults to 1 when there's a divide by zero??

@wildbill: Interesting that it works for you on an Uno, but my Nano simply takes a nap. Also interesting that the divided by zero condition yields the max value for an unsigned long even though the code call for an int. Silent casting??

Silent casting??

The result is a "feature" of Serial.print().

If a C program divides integers by zero, the entire program is undefined, although in
many implementations only the result is undefined.

You should never let an integer be divided by a zero value - that's a bug in your code.

Another similar gotcha is that if signed integer types overflow, the entire program is
undefined... unsigned ints can happily overflow and wrapround however.

In practice what this means is that different implementations do different things when
these "undefined" operations happen, so your code is not portable C/C++. And in
some cases the program will misbehave in strange ways as the compiler is allowed to
assume divide by zero and signed overflow never happen, so that strange behaviours
are manifested (I've seen this on ARM processors, where a signed overflow affected
subsequent shift operator on completely different variable...).

@MarkT:
If a C program divides integers by zero, the entire program is undefined, although in
many implementations only the result is undefined.
Not sure what this means. Does the C Standard say the program is undefined? If so, the the compiler is non-conforming. If not, then it is implementation-defined by the compiler or its libraries.

You should never let an integer be divided by a zero value - that's a bug in your code.
First, as mentioned in my initial post, it's not my code. K&P used it to show how not to write code, even if it works. I've been coding in C for over 40 years, so I do know not to divide by zero.

Given both comments, I think jremington has the real answer. The behavior I'm seeing is the result of the library routine and not the compiler. Still, I wonder why my Nano locks up, but the Uno didn't.

econjack:
@MarkT:
First, as mentioned in my initial post, it's not my code. K&P used it to show how not to write code, even if it works.

I don't see any divide by zero issue in their code - different edition perhaps.

Still, I wonder why my Nano locks up, but the Uno didn't.

Might be IDE version - I'm using 1.6.12.

econjack:
John:
Are you seeing the identity matrix in the second nest loop set? It appears the print() defaults to 1 when there's a divide by zero??

No, I see:

nan nan nan nan nan 
nan 1.00 1.00 1.00 1.00 
nan 1.00 1.00 1.00 1.00 
nan 1.00 1.00 1.00 1.00 
nan 1.00 1.00 1.00 1.00

You only get the identity matrix in integers because when 'i' and 'j' are equal 'i/j' and 'j/i' are both equal to 1 and 1*1 is 1. When 'i' and 'j' are not equal, either 'i/j' or 'j/i' results in zero. Anything times 0 is 0.

Am I the only one thinking nan nan nan nan nan...Batman!

econjack:
If a C program divides integers by zero, the entire program is undefined, although in
many implementations only the result is undefined.
Not sure what this means. Does the C Standard say the program is undefined? If so, the the compiler is non-conforming. If not, then it is implementation-defined by the compiler or its libraries.

No, MarkT is right: if you divide integers by zero, that's undefined behavior, and you're lucky it does anything at all.

https://en.cppreference.com/w/cpp/language/operator_arithmetic#Multiplicative_operators

The binary operator / divides the first operand by the second (after usual arithmetic conversions). For integral operands, it yields the algebraic quotient.
If the second operand is zero, the behavior is undefined, except that if floating-point division is taking place and the type supports IEEE floating-point arithmetic (see std::numeric_limits::is_iec559)

https://en.cppreference.com/w/cpp/language/ub

Undefined behavior

Renders the entire program meaningless if certain rules of the language are violated.

There are no restrictions on the behavior of the program. Examples of undefined behavior are memory accesses outside of array bounds, signed integer overflow, null pointer dereference, [...]
Compilers are not required to diagnose undefined behavior (although many simple situations are diagnosed), and the compiled program is not required to do anything meaningful.

As you can see in this thread, the results will vary between different architectures, different optimization levels, and even different versions of the same compiler on the same hardware.

Pieter

@wildbill I don't see any divide by zero issue in their code - different edition perhaps.

Are you looking at the right book? The code is in the first paragraph on page 1. Note, the book is K&P, not K&R.

econjack:
@wildbill I don't see any divide by zero issue in their code - different edition perhaps.

Are you looking at the right book? The code is in the first paragraph on page 1. Note, the book is K&P, not K&R.

Yes, right book, second edition. Their loops are 1 to N, so that there is no issue with division by zero.

@wildbill: You're right on the original code. Still, it makes me wonder about the divide by zero error. I would think some kind of warning should be issued.

I think the code is too complex for the compiler to figure out the issue. And at runtime, there's nowhere for the Arduino to report it to.

If I turn your code into a normal C program and gcc it on my Mac, the compiler is happy but it does fail with an error when I try to run it.