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.
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.
@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??
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:
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.
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.
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)
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.
@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.