-
Notifications
You must be signed in to change notification settings - Fork 13.3k
Casting a negative char to an int results in a positive number #6010
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
int is 32 bit, char is 8 bit. The 2's complement of -61 is to 195. It's not a bug, now google and learn. |
Char is signed, int is signed. Char goes from -128 to 127, int from -2,147,483,648 to 2,147,483,647. A char can be perfectly represented in an int. Casting a smaller (byte size) signed type to a bigger signed type results in a signed value. At least thats how (almost) all computer languages work. As you can see the test on the Arduino Uno and on the host work as expected. Only the ESP12E is the odd one out |
Casting a negative int to a long also works as expected. Only char doesnt. I suspect the char is implemented as a uint8_t on the ESP12 |
GCC gives a warning when you compile this. That's the root of your problem, you're not really doing a comparison like you think you are in the first case. Please look at GCC's typing and condition checking.
|
Sorry, I'm lost on this one. A char is perfectly able to represent negative numbers. Also, how does that explain the different results on the ESP, Uno and the host? |
To make my point even more clear: Sketch void setup() {
Serial.begin(115200);
Serial.println();
char c = -62;
unsigned char u = -62;
Serial.println(c == u);
} Output:
This shows that a signed and unsigned char are equal on an ESP and not equal on an Arduino Uno. Mybe this is a Arduino IDE issue? |
Even earlephilhower's comment should prove this. GCC is warning that a char can never be negative. Again, that only is the case if it is implemented as unsigned. But that is different from the Uno |
https://stackoverflow.com/questions/436513/char-signed-char-char-unsigned-char It's implementation-defined. |
Thx, thats the answer I was looking for. So the Uno implements a plain char as a signed char and the ESP as a unsigned char. I changed the second Sketch to this: void setup() {
Serial.begin(115200);
Serial.println();
signed char c = -62;
unsigned char u = -62;
Serial.println(c == u);
} Now the ESP and Uno give the same result. |
Example: class MockStream : public Stream {
private:
String mStr;
const char* mCstr;
public:
MockStream(String str) {
mStr = str;
mCstr = mStr.c_str();
}
virtual int available() {
if(*mCstr == 0) return 0;
long charsRead = mCstr - mStr.c_str();
return mStr.length() - charsRead;
}
virtual int read() {
return *mCstr == 0 ? -1 : *mCstr++;
}
virtual int peek() {
return *mCstr;
}
virtual size_t write(uint8_t) {
return 1;
}
};
void setup() {
Serial.begin(115200);
Serial.println();
MockStream stream("[äöü]");
Serial.println(stream.readString());
} Output:
|
Plain char is not guaranteed to be portable across different platforms. The signedness of char is implementation defined, and has always been. Traditionally, i.e. Historically, older compilers used signed char as default, because the ascii table only went up to 127. Then there was the extended ascii table which went up to 255, so that habit changed to unsigned char, mostly with 32bit cpus, and then spread back to uCs. |
Thank you all for explaining this to me. I come from Java and never had that issue with C/C++ until now (seems that in embedded you have to be WAY more careful). I thought all primitive types were defined in C/C++, turns out only char is not (kinda a trap if you ask me :)). Just a couple of points to end this:
Changing the sketch to this fixed my issues: class MockStream : public Stream {
private:
String mStr;
const char* mCstr;
public:
MockStream(String str) {
mStr = str;
mCstr = mStr.c_str();
}
virtual int available() {
if(*mCstr == 0) return 0;
long charsRead = mCstr - mStr.c_str();
return mStr.length() - charsRead;
}
virtual int read() {
return (unsigned char)*mCstr == 0 ? -1 : *mCstr++;
}
virtual int peek() {
return (unsigned char)*mCstr;
}
virtual size_t write(uint8_t) {
return 1;
}
}; Now Uno, ESP and host do exactly the same. |
Force GCC to run with -funsigned-char during host tests to make the PC match the default behaviour used by the xtensa GCC port. As noted in #6010. Thanks @MichaelBrunn3r
Basic Infos
Platform
Settings in IDE
Problem Description
Casting a negative char to an int results in a positive number. The results differ depending on the platform.
Testing on device
Arduino Sketch
Debug Messages
Running on Arduino IDE, Arduino Uno
Running on Arduino IDE, Nodemcu ESP-12E
Testing on host
Cloned newest release. Added file to
TEST_CPP_FILES
. Usedmake test
to run.Catch test file
Debug Messages
The text was updated successfully, but these errors were encountered: