I wanted to show my students the value range of a 16-bit int.
IMHO the following code doesn't behave correctly:
int main()
{
init();
Serial.begin(9600);
int16_t a=32760;
// force var a to be from memory class "auto"
// if you want the address of a variable, the gcc optimizer
// cannot use a register:
// Serial.print("Adress of a: "); Serial.println((unsigned int) &a);
Serial.print("Sizeof a: "); Serial.println(sizeof(a));
while (a<=-32760 || a>=32760)
{
Serial.println(a);
a++;
}
while (1) { }
return 0;
}
The first output shows that the sizeof(a) is 2 (16-bit). The value range is of a 32-bit int.
As far as I can see, this is a problem of the avr-ggc optimizer.
If storage class register is use then int/int16_t gets the value range of a 32-bit value. If storage class auto (variable on stack) is use the programm behaves IMHO correctly.
To show this, I added the statement
Serial.println((unsigned int) &a);
so the optimizer cannot use the storage class register. Then the code behaves correctly.
The data types short int or unsigned int/uint16_t don't have this "flaw".
The installed version is:
arduino\tools\avr-gcc\7.3.0-atmel3.6.1-arduino7/bin/avr-g++
The storage class shouldn't change the value range of a data type. Or do I miss sth.?
Have you checked compiler warnings about type extension or the like? Computations can be done with increased precision until the result is stored in a variable.
It's not clear what you're expecting or getting.
Serial.print() for int has a somewhat sketchy definition:
size_t Print::print(int n, int base)
{
return print((long) n, base);
}
I believe that the behavior of C in the presence of signed integer overflow is "undefined." So anything is "legal"... ( Integer Overflow Basics - Autoconf )
...
In contrast, the C standard says that signed integer overflow leads to undefined behavior where a program can do anything, ...
In practice all known implementations support silent wraparound in this case, so you need not worry about other possibilities.
So with Arduino Uno it differs and "you have to worry about it", because it depends on the storage class.
auto, static, extern support silent wraparound on int, but if the optimizer uses register then the int16_t doesn't wraparound on 32767+1, but extends the value range of a regular int, using 4 Atmel register instead of 2.
ANSI-C guarantees the wraparound only for unsigned integer types.
but the compiler flag "-fwrapv" solves this issue.
Of course declaring the variable volatile or static will also work in this case, because it will use a memory storage.
(see ISO/IEC 9899:1999 (E) Footnote 114:
A volatile declaration may be used to describe an object corresponding to a memory-mapped
input/output port or an object accessed by an asynchronously interrupting function. Actions on
objects so declared shall not be ‘‘optimized out’’ by an implementation or reordered except as
permitted by the rules for evaluating expressions.)
But the problem with undefined behaviour on signed integer overflow isn't addressed with this, because as far as I understand the Integer Overflow Basics it could also happen with a variable in the RAM (even if it will be handled correctly with the actual compiler).