Reputation: 1421
I came from this question where I wanted to write 2 integers to a single byte that were garunteed to be between 0-16 (4 bits each).
Now if I close the file, and run a different program that reads....
for (int i = 0; i < 2; ++i)
{
char byteToRead;
file.seekg(i, std::ios::beg);
file.read(&byteToRead, sizeof(char));
bool correct = file.bad();
unsigned int num1 = (byteToRead >> 4);
unsigned int num2 = (byteToRead & 0x0F);
}
The issue is, sometimes this works but other times I'm having the first number come out negative and the second number is something like 10 or 9 all the time and they were most certainly not the numbers I wrote!
So here, for example, the first two numbers work, but the next number does not. For examplem, the output of the read above would be:
At byte 0, num1 = 5 and num2 = 6
At byte 1, num1 = 4294967289 and num2 = 12
At byte 1, num1 should be 9. It seems the 12 writes fine but the 9 << 4 isn't working. The byteToWrite on my end is byteToWrite -100 'œ''
I checked out this question which has a similar problem I think but I feel like my endian is right here.
Upvotes: 0
Views: 768
Reputation: 141544
The problem originates with byteToRead >> 4
. In C, any arithmetic operations are performed in at least int
precision. So the first thing that happens is that byteToRead
is promoted to int.
These promotions are value-preserving. Your system has plain char
as signed, i.e. having range -128
through to 127
. Your char might have been initially -112
(bit pattern 10010000
), and then after promotion to int it retains its value of -112
(bit pattern 11111...1110010000
).
The right-shift of a negative value is implementation-defined but a common implementation is to do an "arithmetic shift", i.e. perform division by two; so you end up with the result of byteToRead >> 4
being -7
(bit pattern 11111....111001
).
Converting -7
to unsigned int
results in UINT_MAX - 6
which is 4295967289, because unsigned arithmetic is defined as wrapping around mod UINT_MAX+1
.
To fix this you need to convert to unsigned before performing the arithmetic . You could cast (or alias) byteToRead
to unsigned char
, e.g.:
unsigned char byteToRead;
file.read( (char *)&byteToRead, 1 );
Upvotes: 1
Reputation: 544
The right-shift operator preserves the value of the left-most bit. If the left-most bit is 0 before the shift, it will still be 0 after the shift; if it is 1, it will still be 1 after the shift. This allow to preserve the value's sign.
In your case, you combine 9 (0b1001) with 12 (0b1100), so you write 0b10011100 (0x9C). The bit #7 is 1.
When byteToRead is right-shifted, you get 0b11111001 (0xF9), but it is implicitly converted to an int. The convertion from char to int also preserve the value's sign, so it produce 0xFFFFFFF9. Then the implicit int is implicitly converted to a unsigned int. So num1 contains 0xFFFFFFF9 which is 4294967289.
There is 2 solutions:
Upvotes: 1