I usually never post questions but I am at a loss... Is this a bad programming practice or is this a glitch that I just discovered, and How would you recommend handling this situation?
Googled:
A 16-bit integer can store 2**16** (or 65,536) distinct values. In an unsigned representation, these values are the integers between 0 and 65,535; using two's complement, possible values range from −32,768 to 32,767. Hence, a processor with16-bit memory addresses can directly access 64 KB of byte-addressable memory.
I see that it would be impossible to convert an int from a -32768 to positive 32768 as it doesn't exist in a 16-bit integer.
What're your recommendations?
And should there be some kind of warnings in the Arduino Reference?
Test code:
void setup() {
// put your setup code here, to run once:
// initialize serial communication
Serial.begin(115200);
while (!Serial); // wait for Leonardo enumeration, others continue immediately
int16_t a = -32768 ;
int b = -32768 ;
int32_t c = -32768 ;
long d = -32768 ;
Serial.print("abs a = ");
Serial.println(abs(a));
Serial.print("abs b = ");
Serial.println(abs(b));
Serial.print("abs c = ");
Serial.println(abs(c));
Serial.print("abs d = ");
Serial.println(abs(d));
}
void loop() {
// put your main code here, to run repeatedly:
}
Results:
abs a = -32768
abs b = -32768
abs c = 32768
abs d = 32768
There's no signed integral type in C++ where abs() will produce anything useful from the most negative value of that type. You just have to avoid that particular value.
It's just the nature of twos complement math - there are an even number of different values, and one of them is zero. Therefore, there will be one extra value left over that has to go to either the positive values or the negative values, and the nature of twos complement dictates that the negative numbers get the extra value.
If you need to use the full range of an int for some reason, then you'd need to use a long instead of an int to store the values, and just accept that the long is bigger than an int.
The C++ specification says that calling abs() on these kinds of values is undefined behavior. That means that the compiler is allowed to do absolutely anything it wants when it runs into these values - maybe it returns the original value, maybe it returns 0, maybe it calls abort(), whatever. It is always bad practice to rely on undefined behavior. It's bad for portability, and there's no guarantee that even the next version of the compiler will behave the same way.
MHotchin:
There's no signed integral type in C++ where abs() will produce anything useful from the most negative value of that type. You just have to avoid that particular value.
It's just the nature of twos complement math - there are an even number of different values, and one of them is zero. Therefore, there will be one extra value left over that has to go to either the positive values or the negative values, and the nature of twos complement dictates that the negative numbers get the extra value.
If you need to use the full range of an int for some reason, then you'd need to use a long instead of an int to store the values, and just accept that the long is bigger than an int.
The C++ specification says that calling abs() on these kinds of values is undefined behavior. That means that the compiler is allowed to do absolutely anything it wants when it runs into these values - maybe it returns the original value, maybe it returns 0, maybe it calls abort(), whatever. It is always bad practice to rely on undefined behavior. It's bad for portability, and there's no guarantee that even the next version of the compiler will behave the same way.
Thank you. undefined behavior
Makes sense now lol :o
I guess there are several ways to do it.
I decided to just do it in a way that is most effective for this instance as I now realize that it is too unpredictable to rely on the default functionality of abs();
zhomeslice:
I decided to just do it in a way that is most effective for this instance as I now realize that it is too unpredictable to rely on the default functionality of abs();
The 'default functionality of abs()' ?
That implies that abs() could somehow respond in some other way when called with an invalid value.
If you store the result in an 'unsigned' variable it comes out fine:
void setup() {
// put your setup code here, to run once:
// initialize serial communication
Serial.begin(115200);
while (!Serial); // wait for Leonardo enumeration, others continue immediately
int b = -32768 ;
long d = -32768 ;
unsigned int abs_b = abs(b);
unsigned long abs_d = abs(d);
Serial.print("abs(b) = ");
Serial.println(abs(b));
Serial.print("abs_b = ");
Serial.println(abs_b);
Serial.print("abs(d) = ");
Serial.println(abs(d));
Serial.print("abs_d = ");
Serial.println(abs_d);
}
void loop() {}
srnet:
So how should abs() respond, or what value should it return, when called with an invalid value ?
I have now determined that I will take each instance I need to change the value from negative to positive and create my solution to fit its purpose.
For example, my current code uses this: