How are errors (not related to syntax) managed in arduino and in the AVR architecture in general?
up vote
4
down vote
favorite
I was just curious about how the AVR architecture manages errors that would cause a regular desktop program to crash. I'm talking about logical errors for example math problems that are undefined. Such as division by 0 and getting a square root of a negative number. At first I was expecting that by giving an error to the AVR chip it will just return 0. But I ran this program:
void setup() {
Serial.begin(9600);
}
void loop(){
int x = 0;
int p = 50;
int result;
result = p/x;
Serial.println(result);
result = sqrt(-10);
delay(1000);
Serial.println(result);
delay(10000);
}
and got this output:
4294967295
0
After this I got really confused. Why would the result
variable take the maximum value of an unsigned long
? Since I declared an int
the micro controller must have dedicated 2 bytes of dynamic memory to this variable but apparently it somehow managed to get additional 2 bytes to store the data. Could this mean that the AVR chip can corrupt data in other memory locations while allocating new data for the variable that just was fed a undefined in to it? Okay maybe the AVR chip just sets any mathematical nonsense to 4294967295. But no in the example of getting the square root of -10 we see that the value became 0. Although this is probably a function processed by some library so maybe there is some kind of protection against these errors. Also, I tried to run the program above but with a byte
variable for the result
instead of int
.
void setup() {
Serial.begin(9600);
}
void loop(){
int x = 0;
int p = 50;
int result;
result = p/x;
Serial.println(result);
result = sqrt(-10);
delay(1000);
Serial.println(result);
delay(10000);
}
And the result was the same. So, is there some kind of documentation about error management in AVR since this is important to know while development.
P.S. Everything was run on a real Atmega 328p chip on an arduino nano.
arduino-ide avr mathematics error
|
show 2 more comments
up vote
4
down vote
favorite
I was just curious about how the AVR architecture manages errors that would cause a regular desktop program to crash. I'm talking about logical errors for example math problems that are undefined. Such as division by 0 and getting a square root of a negative number. At first I was expecting that by giving an error to the AVR chip it will just return 0. But I ran this program:
void setup() {
Serial.begin(9600);
}
void loop(){
int x = 0;
int p = 50;
int result;
result = p/x;
Serial.println(result);
result = sqrt(-10);
delay(1000);
Serial.println(result);
delay(10000);
}
and got this output:
4294967295
0
After this I got really confused. Why would the result
variable take the maximum value of an unsigned long
? Since I declared an int
the micro controller must have dedicated 2 bytes of dynamic memory to this variable but apparently it somehow managed to get additional 2 bytes to store the data. Could this mean that the AVR chip can corrupt data in other memory locations while allocating new data for the variable that just was fed a undefined in to it? Okay maybe the AVR chip just sets any mathematical nonsense to 4294967295. But no in the example of getting the square root of -10 we see that the value became 0. Although this is probably a function processed by some library so maybe there is some kind of protection against these errors. Also, I tried to run the program above but with a byte
variable for the result
instead of int
.
void setup() {
Serial.begin(9600);
}
void loop(){
int x = 0;
int p = 50;
int result;
result = p/x;
Serial.println(result);
result = sqrt(-10);
delay(1000);
Serial.println(result);
delay(10000);
}
And the result was the same. So, is there some kind of documentation about error management in AVR since this is important to know while development.
P.S. Everything was run on a real Atmega 328p chip on an arduino nano.
arduino-ide avr mathematics error
I think the standard is that it is undefined for integer math. The floating point is according to the IEEE standard and returns a nan, but no exception. Catching an exeption in the Arduino is not possible (I think) : stackoverflow.com/questions/10095591/… When you find strange things with a byte, please give a full sketch that we can try and tell us which arduino board you use.
– Jot
Nov 28 at 19:48
@Jot This is an interesting point but why would a nan be replaced with 4294967295. Apparently they don't have the same binary value. Why did Atmel not include a nan value (that maybe could be disabled through fuses) in their processor architecture. This is a useful troubleshooting feature that is heavily used in all major programming languages.
– Coder_fox
Nov 28 at 19:53
@Jot I used an Arduino nano with an Atmega 328p on board.
– Coder_fox
Nov 28 at 19:54
An integer can not be a 'nan', that is specified for floating point. Please update your question with board and microcontroller, and perhaps a full sketch.
– Jot
Nov 28 at 19:57
I tried to make a sketch myself, and I have different results. Please show your sketch and tell us the results of the sketch that you show. Are you running this in a simulator?
– Jot
Nov 28 at 20:04
|
show 2 more comments
up vote
4
down vote
favorite
up vote
4
down vote
favorite
I was just curious about how the AVR architecture manages errors that would cause a regular desktop program to crash. I'm talking about logical errors for example math problems that are undefined. Such as division by 0 and getting a square root of a negative number. At first I was expecting that by giving an error to the AVR chip it will just return 0. But I ran this program:
void setup() {
Serial.begin(9600);
}
void loop(){
int x = 0;
int p = 50;
int result;
result = p/x;
Serial.println(result);
result = sqrt(-10);
delay(1000);
Serial.println(result);
delay(10000);
}
and got this output:
4294967295
0
After this I got really confused. Why would the result
variable take the maximum value of an unsigned long
? Since I declared an int
the micro controller must have dedicated 2 bytes of dynamic memory to this variable but apparently it somehow managed to get additional 2 bytes to store the data. Could this mean that the AVR chip can corrupt data in other memory locations while allocating new data for the variable that just was fed a undefined in to it? Okay maybe the AVR chip just sets any mathematical nonsense to 4294967295. But no in the example of getting the square root of -10 we see that the value became 0. Although this is probably a function processed by some library so maybe there is some kind of protection against these errors. Also, I tried to run the program above but with a byte
variable for the result
instead of int
.
void setup() {
Serial.begin(9600);
}
void loop(){
int x = 0;
int p = 50;
int result;
result = p/x;
Serial.println(result);
result = sqrt(-10);
delay(1000);
Serial.println(result);
delay(10000);
}
And the result was the same. So, is there some kind of documentation about error management in AVR since this is important to know while development.
P.S. Everything was run on a real Atmega 328p chip on an arduino nano.
arduino-ide avr mathematics error
I was just curious about how the AVR architecture manages errors that would cause a regular desktop program to crash. I'm talking about logical errors for example math problems that are undefined. Such as division by 0 and getting a square root of a negative number. At first I was expecting that by giving an error to the AVR chip it will just return 0. But I ran this program:
void setup() {
Serial.begin(9600);
}
void loop(){
int x = 0;
int p = 50;
int result;
result = p/x;
Serial.println(result);
result = sqrt(-10);
delay(1000);
Serial.println(result);
delay(10000);
}
and got this output:
4294967295
0
After this I got really confused. Why would the result
variable take the maximum value of an unsigned long
? Since I declared an int
the micro controller must have dedicated 2 bytes of dynamic memory to this variable but apparently it somehow managed to get additional 2 bytes to store the data. Could this mean that the AVR chip can corrupt data in other memory locations while allocating new data for the variable that just was fed a undefined in to it? Okay maybe the AVR chip just sets any mathematical nonsense to 4294967295. But no in the example of getting the square root of -10 we see that the value became 0. Although this is probably a function processed by some library so maybe there is some kind of protection against these errors. Also, I tried to run the program above but with a byte
variable for the result
instead of int
.
void setup() {
Serial.begin(9600);
}
void loop(){
int x = 0;
int p = 50;
int result;
result = p/x;
Serial.println(result);
result = sqrt(-10);
delay(1000);
Serial.println(result);
delay(10000);
}
And the result was the same. So, is there some kind of documentation about error management in AVR since this is important to know while development.
P.S. Everything was run on a real Atmega 328p chip on an arduino nano.
arduino-ide avr mathematics error
arduino-ide avr mathematics error
edited Nov 28 at 20:18
asked Nov 28 at 19:39
Coder_fox
36518
36518
I think the standard is that it is undefined for integer math. The floating point is according to the IEEE standard and returns a nan, but no exception. Catching an exeption in the Arduino is not possible (I think) : stackoverflow.com/questions/10095591/… When you find strange things with a byte, please give a full sketch that we can try and tell us which arduino board you use.
– Jot
Nov 28 at 19:48
@Jot This is an interesting point but why would a nan be replaced with 4294967295. Apparently they don't have the same binary value. Why did Atmel not include a nan value (that maybe could be disabled through fuses) in their processor architecture. This is a useful troubleshooting feature that is heavily used in all major programming languages.
– Coder_fox
Nov 28 at 19:53
@Jot I used an Arduino nano with an Atmega 328p on board.
– Coder_fox
Nov 28 at 19:54
An integer can not be a 'nan', that is specified for floating point. Please update your question with board and microcontroller, and perhaps a full sketch.
– Jot
Nov 28 at 19:57
I tried to make a sketch myself, and I have different results. Please show your sketch and tell us the results of the sketch that you show. Are you running this in a simulator?
– Jot
Nov 28 at 20:04
|
show 2 more comments
I think the standard is that it is undefined for integer math. The floating point is according to the IEEE standard and returns a nan, but no exception. Catching an exeption in the Arduino is not possible (I think) : stackoverflow.com/questions/10095591/… When you find strange things with a byte, please give a full sketch that we can try and tell us which arduino board you use.
– Jot
Nov 28 at 19:48
@Jot This is an interesting point but why would a nan be replaced with 4294967295. Apparently they don't have the same binary value. Why did Atmel not include a nan value (that maybe could be disabled through fuses) in their processor architecture. This is a useful troubleshooting feature that is heavily used in all major programming languages.
– Coder_fox
Nov 28 at 19:53
@Jot I used an Arduino nano with an Atmega 328p on board.
– Coder_fox
Nov 28 at 19:54
An integer can not be a 'nan', that is specified for floating point. Please update your question with board and microcontroller, and perhaps a full sketch.
– Jot
Nov 28 at 19:57
I tried to make a sketch myself, and I have different results. Please show your sketch and tell us the results of the sketch that you show. Are you running this in a simulator?
– Jot
Nov 28 at 20:04
I think the standard is that it is undefined for integer math. The floating point is according to the IEEE standard and returns a nan, but no exception. Catching an exeption in the Arduino is not possible (I think) : stackoverflow.com/questions/10095591/… When you find strange things with a byte, please give a full sketch that we can try and tell us which arduino board you use.
– Jot
Nov 28 at 19:48
I think the standard is that it is undefined for integer math. The floating point is according to the IEEE standard and returns a nan, but no exception. Catching an exeption in the Arduino is not possible (I think) : stackoverflow.com/questions/10095591/… When you find strange things with a byte, please give a full sketch that we can try and tell us which arduino board you use.
– Jot
Nov 28 at 19:48
@Jot This is an interesting point but why would a nan be replaced with 4294967295. Apparently they don't have the same binary value. Why did Atmel not include a nan value (that maybe could be disabled through fuses) in their processor architecture. This is a useful troubleshooting feature that is heavily used in all major programming languages.
– Coder_fox
Nov 28 at 19:53
@Jot This is an interesting point but why would a nan be replaced with 4294967295. Apparently they don't have the same binary value. Why did Atmel not include a nan value (that maybe could be disabled through fuses) in their processor architecture. This is a useful troubleshooting feature that is heavily used in all major programming languages.
– Coder_fox
Nov 28 at 19:53
@Jot I used an Arduino nano with an Atmega 328p on board.
– Coder_fox
Nov 28 at 19:54
@Jot I used an Arduino nano with an Atmega 328p on board.
– Coder_fox
Nov 28 at 19:54
An integer can not be a 'nan', that is specified for floating point. Please update your question with board and microcontroller, and perhaps a full sketch.
– Jot
Nov 28 at 19:57
An integer can not be a 'nan', that is specified for floating point. Please update your question with board and microcontroller, and perhaps a full sketch.
– Jot
Nov 28 at 19:57
I tried to make a sketch myself, and I have different results. Please show your sketch and tell us the results of the sketch that you show. Are you running this in a simulator?
– Jot
Nov 28 at 20:04
I tried to make a sketch myself, and I have different results. Please show your sketch and tell us the results of the sketch that you show. Are you running this in a simulator?
– Jot
Nov 28 at 20:04
|
show 2 more comments
2 Answers
2
active
oldest
votes
up vote
8
down vote
accepted
The simple answer is: they are not handled at all.
According to the C and C++ standards, what you are invoking is called
undefined behavior, meaning anything can happen. In practical
terms, it means that the compiler can do its optimizations assuming
you will never invoke undefined behavior, and completely disregard what
could happen if the assumption does not hold. The generated code could
be completely broken, it won't be the compiler's fault.
The AVR architecture cannot do divisions, nor floating point math. Thus
all the errors you have here happen at the software level. Note that
sqrt(-10)
is not an error (it's NaN, which is correctly handled by
the avr-libc), but converting that to an int (which you do when you
assign it to result
) is an error.
If you really want to know the details of what happens with each of
these errors, there is only one option: you have to disassemble the
executable program and read the assembly listing.
Thanks for the great explanation.
– Coder_fox
Nov 28 at 20:41
3
"... undefined behavior, meaning anything can happen." - It's worth emphasizing that this is completely a C/C++ thing, unrelated to the AVR chips. The exact same warning applies to code running on, say, an Intel i5.
– marcelm
Nov 28 at 22:16
1
Also worth noting that while the C++ standard states that the compiler can do anything, C++ compilers typically define additional behavior beyond the C++ specification. For example, Visual Studio's compiler defines the behavior ofx/0
which was undefined in the C++ spec, based on what it knows about the architecture. This is really useful when you want to do something the spec forbids, but your particular compiler permits, like gcc extensions.
– Cort Ammon
Nov 29 at 0:12
add a comment |
up vote
2
down vote
About the number 4294967295
To my surprise, the sketch produces indeed the number 4294967295. That is not okay.
It turns out that the compiler knows the numbers and tries to do the math itself (instead of runtime).
At runtime, the 50 / 0
results into a signed integer of -1.
When a byte is used, the 50 / 0
results into 255.
The result of that calculation is a variable with only 1's. That is 0xFFFF for a 16-bits variable. That results into 65535 for a unsigned integer and -1 for a signed integer and 255 for a unsigned byte.
So far, that is okay. It makes sense.
However, when the compiler tries to do the math itself with 50 / 0
, it turns the result into a 32-bit variable of 0xFFFFFFFF. The compiler does not put the math into the binary result, but calls the Serial.println() directly with a 32-bit unsigned long of 0xFFFFFFFF, which is 4294967295.
The reason is not the variable type of the variable 'result
', but the division with 'p/x
'. The compiler thinks it is better to make a 32-bit integer when the math is done with two 16-bit integers.
I think that when the compiler optimizes, the result should be same as a runtime calculation.
There is an interesting discussion below this answer. @EdgarBonet calls the compiler behaviour weird but not a bug.
Below is a test sketch that produces 4294967295 with Arduino 1.8.7 for an Arduino Uno:
void setup() {
Serial.begin(9600);
}
void loop(){
int x = 0;
int p = 50;
int result;
result = p/x;
Serial.println(result);
delay(5000);
}
The code outputs normal results when 'x' or 'p' is calculated, for example the result from a function, or when one of the three variables is made volatile.
1
The handling of undefined behavior by the compiler is never a bug because, by definition, whatever the compiler does is acceptable per the language standard.
– Edgar Bonet
Nov 29 at 8:33
@EdgarBonet I think it should do whatever in the same way. In your opinion the compiler can insert code to whistle a tune when dividing by zero? I don't think so. Changing an int to unsigned long is too weird in my opinion.
– Jot
Nov 29 at 11:06
1
Although the compiler may not deliberately do silly things, strange things do happen when you invoke undefined behavior. See for example the beginning of this blog post (the whole post series is a must-read for any C or C++ programmer). Weird? Yes. Bug? No.
– Edgar Bonet
Nov 29 at 11:56
@EdgarBonet, thanks, interesting to read that. The things they mention are straightforward. What happens here is that runtime a certain function is called, and when the compiler optimizes it, a different (overloaded) function is called. That is one step further into weirdness.
– Jot
Nov 29 at 16:34
"In your opinion the compiler can insert code to whistle a tune when dividing by zero? I don't think so." - When you invoke Undefined Behaviour, everything goes, including unexpectedly large variables, tune whistling, or formatting your hard drive. Welcome to the wonderful world of C and C++. It would be interesting though to see if the same "variable promotion" occurs without UB.
– marcelm
Nov 29 at 23:06
|
show 1 more comment
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
8
down vote
accepted
The simple answer is: they are not handled at all.
According to the C and C++ standards, what you are invoking is called
undefined behavior, meaning anything can happen. In practical
terms, it means that the compiler can do its optimizations assuming
you will never invoke undefined behavior, and completely disregard what
could happen if the assumption does not hold. The generated code could
be completely broken, it won't be the compiler's fault.
The AVR architecture cannot do divisions, nor floating point math. Thus
all the errors you have here happen at the software level. Note that
sqrt(-10)
is not an error (it's NaN, which is correctly handled by
the avr-libc), but converting that to an int (which you do when you
assign it to result
) is an error.
If you really want to know the details of what happens with each of
these errors, there is only one option: you have to disassemble the
executable program and read the assembly listing.
Thanks for the great explanation.
– Coder_fox
Nov 28 at 20:41
3
"... undefined behavior, meaning anything can happen." - It's worth emphasizing that this is completely a C/C++ thing, unrelated to the AVR chips. The exact same warning applies to code running on, say, an Intel i5.
– marcelm
Nov 28 at 22:16
1
Also worth noting that while the C++ standard states that the compiler can do anything, C++ compilers typically define additional behavior beyond the C++ specification. For example, Visual Studio's compiler defines the behavior ofx/0
which was undefined in the C++ spec, based on what it knows about the architecture. This is really useful when you want to do something the spec forbids, but your particular compiler permits, like gcc extensions.
– Cort Ammon
Nov 29 at 0:12
add a comment |
up vote
8
down vote
accepted
The simple answer is: they are not handled at all.
According to the C and C++ standards, what you are invoking is called
undefined behavior, meaning anything can happen. In practical
terms, it means that the compiler can do its optimizations assuming
you will never invoke undefined behavior, and completely disregard what
could happen if the assumption does not hold. The generated code could
be completely broken, it won't be the compiler's fault.
The AVR architecture cannot do divisions, nor floating point math. Thus
all the errors you have here happen at the software level. Note that
sqrt(-10)
is not an error (it's NaN, which is correctly handled by
the avr-libc), but converting that to an int (which you do when you
assign it to result
) is an error.
If you really want to know the details of what happens with each of
these errors, there is only one option: you have to disassemble the
executable program and read the assembly listing.
Thanks for the great explanation.
– Coder_fox
Nov 28 at 20:41
3
"... undefined behavior, meaning anything can happen." - It's worth emphasizing that this is completely a C/C++ thing, unrelated to the AVR chips. The exact same warning applies to code running on, say, an Intel i5.
– marcelm
Nov 28 at 22:16
1
Also worth noting that while the C++ standard states that the compiler can do anything, C++ compilers typically define additional behavior beyond the C++ specification. For example, Visual Studio's compiler defines the behavior ofx/0
which was undefined in the C++ spec, based on what it knows about the architecture. This is really useful when you want to do something the spec forbids, but your particular compiler permits, like gcc extensions.
– Cort Ammon
Nov 29 at 0:12
add a comment |
up vote
8
down vote
accepted
up vote
8
down vote
accepted
The simple answer is: they are not handled at all.
According to the C and C++ standards, what you are invoking is called
undefined behavior, meaning anything can happen. In practical
terms, it means that the compiler can do its optimizations assuming
you will never invoke undefined behavior, and completely disregard what
could happen if the assumption does not hold. The generated code could
be completely broken, it won't be the compiler's fault.
The AVR architecture cannot do divisions, nor floating point math. Thus
all the errors you have here happen at the software level. Note that
sqrt(-10)
is not an error (it's NaN, which is correctly handled by
the avr-libc), but converting that to an int (which you do when you
assign it to result
) is an error.
If you really want to know the details of what happens with each of
these errors, there is only one option: you have to disassemble the
executable program and read the assembly listing.
The simple answer is: they are not handled at all.
According to the C and C++ standards, what you are invoking is called
undefined behavior, meaning anything can happen. In practical
terms, it means that the compiler can do its optimizations assuming
you will never invoke undefined behavior, and completely disregard what
could happen if the assumption does not hold. The generated code could
be completely broken, it won't be the compiler's fault.
The AVR architecture cannot do divisions, nor floating point math. Thus
all the errors you have here happen at the software level. Note that
sqrt(-10)
is not an error (it's NaN, which is correctly handled by
the avr-libc), but converting that to an int (which you do when you
assign it to result
) is an error.
If you really want to know the details of what happens with each of
these errors, there is only one option: you have to disassemble the
executable program and read the assembly listing.
answered Nov 28 at 20:36
Edgar Bonet
23.7k22344
23.7k22344
Thanks for the great explanation.
– Coder_fox
Nov 28 at 20:41
3
"... undefined behavior, meaning anything can happen." - It's worth emphasizing that this is completely a C/C++ thing, unrelated to the AVR chips. The exact same warning applies to code running on, say, an Intel i5.
– marcelm
Nov 28 at 22:16
1
Also worth noting that while the C++ standard states that the compiler can do anything, C++ compilers typically define additional behavior beyond the C++ specification. For example, Visual Studio's compiler defines the behavior ofx/0
which was undefined in the C++ spec, based on what it knows about the architecture. This is really useful when you want to do something the spec forbids, but your particular compiler permits, like gcc extensions.
– Cort Ammon
Nov 29 at 0:12
add a comment |
Thanks for the great explanation.
– Coder_fox
Nov 28 at 20:41
3
"... undefined behavior, meaning anything can happen." - It's worth emphasizing that this is completely a C/C++ thing, unrelated to the AVR chips. The exact same warning applies to code running on, say, an Intel i5.
– marcelm
Nov 28 at 22:16
1
Also worth noting that while the C++ standard states that the compiler can do anything, C++ compilers typically define additional behavior beyond the C++ specification. For example, Visual Studio's compiler defines the behavior ofx/0
which was undefined in the C++ spec, based on what it knows about the architecture. This is really useful when you want to do something the spec forbids, but your particular compiler permits, like gcc extensions.
– Cort Ammon
Nov 29 at 0:12
Thanks for the great explanation.
– Coder_fox
Nov 28 at 20:41
Thanks for the great explanation.
– Coder_fox
Nov 28 at 20:41
3
3
"... undefined behavior, meaning anything can happen." - It's worth emphasizing that this is completely a C/C++ thing, unrelated to the AVR chips. The exact same warning applies to code running on, say, an Intel i5.
– marcelm
Nov 28 at 22:16
"... undefined behavior, meaning anything can happen." - It's worth emphasizing that this is completely a C/C++ thing, unrelated to the AVR chips. The exact same warning applies to code running on, say, an Intel i5.
– marcelm
Nov 28 at 22:16
1
1
Also worth noting that while the C++ standard states that the compiler can do anything, C++ compilers typically define additional behavior beyond the C++ specification. For example, Visual Studio's compiler defines the behavior of
x/0
which was undefined in the C++ spec, based on what it knows about the architecture. This is really useful when you want to do something the spec forbids, but your particular compiler permits, like gcc extensions.– Cort Ammon
Nov 29 at 0:12
Also worth noting that while the C++ standard states that the compiler can do anything, C++ compilers typically define additional behavior beyond the C++ specification. For example, Visual Studio's compiler defines the behavior of
x/0
which was undefined in the C++ spec, based on what it knows about the architecture. This is really useful when you want to do something the spec forbids, but your particular compiler permits, like gcc extensions.– Cort Ammon
Nov 29 at 0:12
add a comment |
up vote
2
down vote
About the number 4294967295
To my surprise, the sketch produces indeed the number 4294967295. That is not okay.
It turns out that the compiler knows the numbers and tries to do the math itself (instead of runtime).
At runtime, the 50 / 0
results into a signed integer of -1.
When a byte is used, the 50 / 0
results into 255.
The result of that calculation is a variable with only 1's. That is 0xFFFF for a 16-bits variable. That results into 65535 for a unsigned integer and -1 for a signed integer and 255 for a unsigned byte.
So far, that is okay. It makes sense.
However, when the compiler tries to do the math itself with 50 / 0
, it turns the result into a 32-bit variable of 0xFFFFFFFF. The compiler does not put the math into the binary result, but calls the Serial.println() directly with a 32-bit unsigned long of 0xFFFFFFFF, which is 4294967295.
The reason is not the variable type of the variable 'result
', but the division with 'p/x
'. The compiler thinks it is better to make a 32-bit integer when the math is done with two 16-bit integers.
I think that when the compiler optimizes, the result should be same as a runtime calculation.
There is an interesting discussion below this answer. @EdgarBonet calls the compiler behaviour weird but not a bug.
Below is a test sketch that produces 4294967295 with Arduino 1.8.7 for an Arduino Uno:
void setup() {
Serial.begin(9600);
}
void loop(){
int x = 0;
int p = 50;
int result;
result = p/x;
Serial.println(result);
delay(5000);
}
The code outputs normal results when 'x' or 'p' is calculated, for example the result from a function, or when one of the three variables is made volatile.
1
The handling of undefined behavior by the compiler is never a bug because, by definition, whatever the compiler does is acceptable per the language standard.
– Edgar Bonet
Nov 29 at 8:33
@EdgarBonet I think it should do whatever in the same way. In your opinion the compiler can insert code to whistle a tune when dividing by zero? I don't think so. Changing an int to unsigned long is too weird in my opinion.
– Jot
Nov 29 at 11:06
1
Although the compiler may not deliberately do silly things, strange things do happen when you invoke undefined behavior. See for example the beginning of this blog post (the whole post series is a must-read for any C or C++ programmer). Weird? Yes. Bug? No.
– Edgar Bonet
Nov 29 at 11:56
@EdgarBonet, thanks, interesting to read that. The things they mention are straightforward. What happens here is that runtime a certain function is called, and when the compiler optimizes it, a different (overloaded) function is called. That is one step further into weirdness.
– Jot
Nov 29 at 16:34
"In your opinion the compiler can insert code to whistle a tune when dividing by zero? I don't think so." - When you invoke Undefined Behaviour, everything goes, including unexpectedly large variables, tune whistling, or formatting your hard drive. Welcome to the wonderful world of C and C++. It would be interesting though to see if the same "variable promotion" occurs without UB.
– marcelm
Nov 29 at 23:06
|
show 1 more comment
up vote
2
down vote
About the number 4294967295
To my surprise, the sketch produces indeed the number 4294967295. That is not okay.
It turns out that the compiler knows the numbers and tries to do the math itself (instead of runtime).
At runtime, the 50 / 0
results into a signed integer of -1.
When a byte is used, the 50 / 0
results into 255.
The result of that calculation is a variable with only 1's. That is 0xFFFF for a 16-bits variable. That results into 65535 for a unsigned integer and -1 for a signed integer and 255 for a unsigned byte.
So far, that is okay. It makes sense.
However, when the compiler tries to do the math itself with 50 / 0
, it turns the result into a 32-bit variable of 0xFFFFFFFF. The compiler does not put the math into the binary result, but calls the Serial.println() directly with a 32-bit unsigned long of 0xFFFFFFFF, which is 4294967295.
The reason is not the variable type of the variable 'result
', but the division with 'p/x
'. The compiler thinks it is better to make a 32-bit integer when the math is done with two 16-bit integers.
I think that when the compiler optimizes, the result should be same as a runtime calculation.
There is an interesting discussion below this answer. @EdgarBonet calls the compiler behaviour weird but not a bug.
Below is a test sketch that produces 4294967295 with Arduino 1.8.7 for an Arduino Uno:
void setup() {
Serial.begin(9600);
}
void loop(){
int x = 0;
int p = 50;
int result;
result = p/x;
Serial.println(result);
delay(5000);
}
The code outputs normal results when 'x' or 'p' is calculated, for example the result from a function, or when one of the three variables is made volatile.
1
The handling of undefined behavior by the compiler is never a bug because, by definition, whatever the compiler does is acceptable per the language standard.
– Edgar Bonet
Nov 29 at 8:33
@EdgarBonet I think it should do whatever in the same way. In your opinion the compiler can insert code to whistle a tune when dividing by zero? I don't think so. Changing an int to unsigned long is too weird in my opinion.
– Jot
Nov 29 at 11:06
1
Although the compiler may not deliberately do silly things, strange things do happen when you invoke undefined behavior. See for example the beginning of this blog post (the whole post series is a must-read for any C or C++ programmer). Weird? Yes. Bug? No.
– Edgar Bonet
Nov 29 at 11:56
@EdgarBonet, thanks, interesting to read that. The things they mention are straightforward. What happens here is that runtime a certain function is called, and when the compiler optimizes it, a different (overloaded) function is called. That is one step further into weirdness.
– Jot
Nov 29 at 16:34
"In your opinion the compiler can insert code to whistle a tune when dividing by zero? I don't think so." - When you invoke Undefined Behaviour, everything goes, including unexpectedly large variables, tune whistling, or formatting your hard drive. Welcome to the wonderful world of C and C++. It would be interesting though to see if the same "variable promotion" occurs without UB.
– marcelm
Nov 29 at 23:06
|
show 1 more comment
up vote
2
down vote
up vote
2
down vote
About the number 4294967295
To my surprise, the sketch produces indeed the number 4294967295. That is not okay.
It turns out that the compiler knows the numbers and tries to do the math itself (instead of runtime).
At runtime, the 50 / 0
results into a signed integer of -1.
When a byte is used, the 50 / 0
results into 255.
The result of that calculation is a variable with only 1's. That is 0xFFFF for a 16-bits variable. That results into 65535 for a unsigned integer and -1 for a signed integer and 255 for a unsigned byte.
So far, that is okay. It makes sense.
However, when the compiler tries to do the math itself with 50 / 0
, it turns the result into a 32-bit variable of 0xFFFFFFFF. The compiler does not put the math into the binary result, but calls the Serial.println() directly with a 32-bit unsigned long of 0xFFFFFFFF, which is 4294967295.
The reason is not the variable type of the variable 'result
', but the division with 'p/x
'. The compiler thinks it is better to make a 32-bit integer when the math is done with two 16-bit integers.
I think that when the compiler optimizes, the result should be same as a runtime calculation.
There is an interesting discussion below this answer. @EdgarBonet calls the compiler behaviour weird but not a bug.
Below is a test sketch that produces 4294967295 with Arduino 1.8.7 for an Arduino Uno:
void setup() {
Serial.begin(9600);
}
void loop(){
int x = 0;
int p = 50;
int result;
result = p/x;
Serial.println(result);
delay(5000);
}
The code outputs normal results when 'x' or 'p' is calculated, for example the result from a function, or when one of the three variables is made volatile.
About the number 4294967295
To my surprise, the sketch produces indeed the number 4294967295. That is not okay.
It turns out that the compiler knows the numbers and tries to do the math itself (instead of runtime).
At runtime, the 50 / 0
results into a signed integer of -1.
When a byte is used, the 50 / 0
results into 255.
The result of that calculation is a variable with only 1's. That is 0xFFFF for a 16-bits variable. That results into 65535 for a unsigned integer and -1 for a signed integer and 255 for a unsigned byte.
So far, that is okay. It makes sense.
However, when the compiler tries to do the math itself with 50 / 0
, it turns the result into a 32-bit variable of 0xFFFFFFFF. The compiler does not put the math into the binary result, but calls the Serial.println() directly with a 32-bit unsigned long of 0xFFFFFFFF, which is 4294967295.
The reason is not the variable type of the variable 'result
', but the division with 'p/x
'. The compiler thinks it is better to make a 32-bit integer when the math is done with two 16-bit integers.
I think that when the compiler optimizes, the result should be same as a runtime calculation.
There is an interesting discussion below this answer. @EdgarBonet calls the compiler behaviour weird but not a bug.
Below is a test sketch that produces 4294967295 with Arduino 1.8.7 for an Arduino Uno:
void setup() {
Serial.begin(9600);
}
void loop(){
int x = 0;
int p = 50;
int result;
result = p/x;
Serial.println(result);
delay(5000);
}
The code outputs normal results when 'x' or 'p' is calculated, for example the result from a function, or when one of the three variables is made volatile.
edited Nov 29 at 16:38
answered Nov 29 at 6:40
Jot
2,0341517
2,0341517
1
The handling of undefined behavior by the compiler is never a bug because, by definition, whatever the compiler does is acceptable per the language standard.
– Edgar Bonet
Nov 29 at 8:33
@EdgarBonet I think it should do whatever in the same way. In your opinion the compiler can insert code to whistle a tune when dividing by zero? I don't think so. Changing an int to unsigned long is too weird in my opinion.
– Jot
Nov 29 at 11:06
1
Although the compiler may not deliberately do silly things, strange things do happen when you invoke undefined behavior. See for example the beginning of this blog post (the whole post series is a must-read for any C or C++ programmer). Weird? Yes. Bug? No.
– Edgar Bonet
Nov 29 at 11:56
@EdgarBonet, thanks, interesting to read that. The things they mention are straightforward. What happens here is that runtime a certain function is called, and when the compiler optimizes it, a different (overloaded) function is called. That is one step further into weirdness.
– Jot
Nov 29 at 16:34
"In your opinion the compiler can insert code to whistle a tune when dividing by zero? I don't think so." - When you invoke Undefined Behaviour, everything goes, including unexpectedly large variables, tune whistling, or formatting your hard drive. Welcome to the wonderful world of C and C++. It would be interesting though to see if the same "variable promotion" occurs without UB.
– marcelm
Nov 29 at 23:06
|
show 1 more comment
1
The handling of undefined behavior by the compiler is never a bug because, by definition, whatever the compiler does is acceptable per the language standard.
– Edgar Bonet
Nov 29 at 8:33
@EdgarBonet I think it should do whatever in the same way. In your opinion the compiler can insert code to whistle a tune when dividing by zero? I don't think so. Changing an int to unsigned long is too weird in my opinion.
– Jot
Nov 29 at 11:06
1
Although the compiler may not deliberately do silly things, strange things do happen when you invoke undefined behavior. See for example the beginning of this blog post (the whole post series is a must-read for any C or C++ programmer). Weird? Yes. Bug? No.
– Edgar Bonet
Nov 29 at 11:56
@EdgarBonet, thanks, interesting to read that. The things they mention are straightforward. What happens here is that runtime a certain function is called, and when the compiler optimizes it, a different (overloaded) function is called. That is one step further into weirdness.
– Jot
Nov 29 at 16:34
"In your opinion the compiler can insert code to whistle a tune when dividing by zero? I don't think so." - When you invoke Undefined Behaviour, everything goes, including unexpectedly large variables, tune whistling, or formatting your hard drive. Welcome to the wonderful world of C and C++. It would be interesting though to see if the same "variable promotion" occurs without UB.
– marcelm
Nov 29 at 23:06
1
1
The handling of undefined behavior by the compiler is never a bug because, by definition, whatever the compiler does is acceptable per the language standard.
– Edgar Bonet
Nov 29 at 8:33
The handling of undefined behavior by the compiler is never a bug because, by definition, whatever the compiler does is acceptable per the language standard.
– Edgar Bonet
Nov 29 at 8:33
@EdgarBonet I think it should do whatever in the same way. In your opinion the compiler can insert code to whistle a tune when dividing by zero? I don't think so. Changing an int to unsigned long is too weird in my opinion.
– Jot
Nov 29 at 11:06
@EdgarBonet I think it should do whatever in the same way. In your opinion the compiler can insert code to whistle a tune when dividing by zero? I don't think so. Changing an int to unsigned long is too weird in my opinion.
– Jot
Nov 29 at 11:06
1
1
Although the compiler may not deliberately do silly things, strange things do happen when you invoke undefined behavior. See for example the beginning of this blog post (the whole post series is a must-read for any C or C++ programmer). Weird? Yes. Bug? No.
– Edgar Bonet
Nov 29 at 11:56
Although the compiler may not deliberately do silly things, strange things do happen when you invoke undefined behavior. See for example the beginning of this blog post (the whole post series is a must-read for any C or C++ programmer). Weird? Yes. Bug? No.
– Edgar Bonet
Nov 29 at 11:56
@EdgarBonet, thanks, interesting to read that. The things they mention are straightforward. What happens here is that runtime a certain function is called, and when the compiler optimizes it, a different (overloaded) function is called. That is one step further into weirdness.
– Jot
Nov 29 at 16:34
@EdgarBonet, thanks, interesting to read that. The things they mention are straightforward. What happens here is that runtime a certain function is called, and when the compiler optimizes it, a different (overloaded) function is called. That is one step further into weirdness.
– Jot
Nov 29 at 16:34
"In your opinion the compiler can insert code to whistle a tune when dividing by zero? I don't think so." - When you invoke Undefined Behaviour, everything goes, including unexpectedly large variables, tune whistling, or formatting your hard drive. Welcome to the wonderful world of C and C++. It would be interesting though to see if the same "variable promotion" occurs without UB.
– marcelm
Nov 29 at 23:06
"In your opinion the compiler can insert code to whistle a tune when dividing by zero? I don't think so." - When you invoke Undefined Behaviour, everything goes, including unexpectedly large variables, tune whistling, or formatting your hard drive. Welcome to the wonderful world of C and C++. It would be interesting though to see if the same "variable promotion" occurs without UB.
– marcelm
Nov 29 at 23:06
|
show 1 more comment
Thanks for contributing an answer to Arduino Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2farduino.stackexchange.com%2fquestions%2f58301%2fhow-are-errors-not-related-to-syntax-managed-in-arduino-and-in-the-avr-archite%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
I think the standard is that it is undefined for integer math. The floating point is according to the IEEE standard and returns a nan, but no exception. Catching an exeption in the Arduino is not possible (I think) : stackoverflow.com/questions/10095591/… When you find strange things with a byte, please give a full sketch that we can try and tell us which arduino board you use.
– Jot
Nov 28 at 19:48
@Jot This is an interesting point but why would a nan be replaced with 4294967295. Apparently they don't have the same binary value. Why did Atmel not include a nan value (that maybe could be disabled through fuses) in their processor architecture. This is a useful troubleshooting feature that is heavily used in all major programming languages.
– Coder_fox
Nov 28 at 19:53
@Jot I used an Arduino nano with an Atmega 328p on board.
– Coder_fox
Nov 28 at 19:54
An integer can not be a 'nan', that is specified for floating point. Please update your question with board and microcontroller, and perhaps a full sketch.
– Jot
Nov 28 at 19:57
I tried to make a sketch myself, and I have different results. Please show your sketch and tell us the results of the sketch that you show. Are you running this in a simulator?
– Jot
Nov 28 at 20:04