P
petek1976
Hi,
The C++ standard doesguarantee any representation for
signed integers which of course means it is implementation defined to
right-shift a signed numeric type (no guarantee of sign-extension
etc.) So if I were defining an interface that expected a
sequence of bytes in big-endian byte order (no matter what the
architecture/platform) and I wanted to convert a sequence of unsigned
char* to a signed number do I really have to do this:
unsigned long ExtractUInt32M(const unsigned char *apnOctets)
{
const unsigned char *lpnOctets = apnOctets;
unsigned long lnValue = *(lpnOctets++);
lnValue = (lnValue << 8) | *(lpnOctets++);
lnValue = (lnValue << 8) | *(lpnOctets++);
lnValue = (lnValue << 8) | *(lpnOctets);
return lnValue;
}
long ExtractSInt32M(const unsigned char *apnOctets)
{
unsigned long lnBaseBits = ExtractUInt32M(apnOctets); // treat as
unsigned first
long lnValue;
if ((lnBaseBits & 0x80000000) != 0) // Check for negative value
{
lnValue = lnBaseBits - 2147483648; // line 7
lnValue -= 2147483648; // line 8
}
else
{
lnValue = lnBaseBits;
}
return lnValue;
}
Notice the lines that subtract 2^31. This is all based on the fact
that a negative number in twos complement is represented as 2^N -
abs(value). First 2^32 (4294967296) is beyond the guaranteed range for
either a long or an unsigned long in C++. Second the C++ language does
not define what happens if you convert an out of range value long (or
any other integer type). In our case if the two's complement number
represents a negative value, it is undefined what will happen if I
were to assign lnBaseBits directly to lnValue. I solve both of these
issues by
performing the subtraction in two steps. 2^31 is subtracted from
lnBaseBits. Since that is a positive value which is guaranteed to be
within the range of a long, I can safely assign that result to
lnValue. In line 8 the process is completed by subtracting 2^31 again,
which will result in the correct negative value. Of course what I am
doing here is still not really portable since the standard only
guarantees a long value of 2147483647. Isn't there an easier way that
does not violate the C++ standard and stil be portable? Will the next
revision of the standard make such a task easier by doing something
like guaranteeing the representation of a signed number so that bit-
shifting will result in sign-extension?
The main point it that I do not want to rely on the underlying data-
representation of a particular machine for portability reasons.
The C++ standard doesguarantee any representation for
signed integers which of course means it is implementation defined to
right-shift a signed numeric type (no guarantee of sign-extension
etc.) So if I were defining an interface that expected a
sequence of bytes in big-endian byte order (no matter what the
architecture/platform) and I wanted to convert a sequence of unsigned
char* to a signed number do I really have to do this:
unsigned long ExtractUInt32M(const unsigned char *apnOctets)
{
const unsigned char *lpnOctets = apnOctets;
unsigned long lnValue = *(lpnOctets++);
lnValue = (lnValue << 8) | *(lpnOctets++);
lnValue = (lnValue << 8) | *(lpnOctets++);
lnValue = (lnValue << 8) | *(lpnOctets);
return lnValue;
}
long ExtractSInt32M(const unsigned char *apnOctets)
{
unsigned long lnBaseBits = ExtractUInt32M(apnOctets); // treat as
unsigned first
long lnValue;
if ((lnBaseBits & 0x80000000) != 0) // Check for negative value
{
lnValue = lnBaseBits - 2147483648; // line 7
lnValue -= 2147483648; // line 8
}
else
{
lnValue = lnBaseBits;
}
return lnValue;
}
Notice the lines that subtract 2^31. This is all based on the fact
that a negative number in twos complement is represented as 2^N -
abs(value). First 2^32 (4294967296) is beyond the guaranteed range for
either a long or an unsigned long in C++. Second the C++ language does
not define what happens if you convert an out of range value long (or
any other integer type). In our case if the two's complement number
represents a negative value, it is undefined what will happen if I
were to assign lnBaseBits directly to lnValue. I solve both of these
issues by
performing the subtraction in two steps. 2^31 is subtracted from
lnBaseBits. Since that is a positive value which is guaranteed to be
within the range of a long, I can safely assign that result to
lnValue. In line 8 the process is completed by subtracting 2^31 again,
which will result in the correct negative value. Of course what I am
doing here is still not really portable since the standard only
guarantees a long value of 2147483647. Isn't there an easier way that
does not violate the C++ standard and stil be portable? Will the next
revision of the standard make such a task easier by doing something
like guaranteeing the representation of a signed number so that bit-
shifting will result in sign-extension?
The main point it that I do not want to rely on the underlying data-
representation of a particular machine for portability reasons.