I am studying a C++ code in Arduino example for reading external data. The code use casting (int16_t) and also (int).
int16_t is fixed integer type, but I don’t understand it’s purpose in the code.
_vRaw = (int)(int16_t)(Wire.read() | Wire.read() << 8);
Is there any difference if I write like below?
_vRaw = (int)(Wire.read() | Wire.read() << 8);
I’ve written code like this before so I can explain the purpose of the cast to
The Arduino command
Wire.read() generally returns a number between 0 and 255 representing a byte read from the I2C device.
Sometimes, two of the bytes you read from a device will represent a single signed 16-bit number using Two’s complement. So, for example, if the first byte is 1 and the second byte is 2, we want to interpret that as a value of 1 + 2*256 = 513. If both bytes are 255, we want to interpret that as -1.
Let’s assume that your code is being compiled for a system where
int is 32-bit (almost any 32-bit microcontroller), and both bytes are 255, and think about this expression:
(Wire.read() | Wire.read() << 8)
The value of this expression would simply be
255 | (255 << 8) which is 65535. That is bad; we wanted the value to be -1.
The easy way to fix that bug is to add a cast to
(int16_t)(Wire.read() | Wire.read() << 8)
When we cast 65535 to an
int16_t type, we get -1 because an
int16_t cannot hold 65535 (it can only hold values from -32768 to 32767). (The C++ standard does not guarantee that we get -1, but it is implementation defined behavior so you can check the manual of your compiler if you want to make sure.) If we later cast it back to an
int (either with an explicit cast or by setting some
int equal to it), the compiler will do the correct signed extension operation and give us an
int with a value of -1.
By the way, I’m not confident that your compiler will run the two calls to
Wire.read() in the right order unless you have a semicolon between then. Also, the final cast to
int is probably not needed. So I would rewrite the code to be something like this:
uint8_t lsb = Wire.read(); _vRaw = (int16_t)(lsb | Wire.read() << 8);