I'm looking for the exact rules according to standard of casting an integer type of signed type to an unsigned integer of the same rank, as well as the contrary (unsigned to signed).
ie:
signed char to unsigned char
unsigned int to signed int
etc...
but not signed char to unsigned int.
In particular, I want to know what happens on machines that do, or don't, use 2's complement. How they behave differently.
Is it safe to write:
Code:
unsigned int max_value = -1;
On any architecture?
The answers I found on the internet were too shady for my tastes.
Is your question related to IO?
Read this C++ FAQ LITE article at parashift by Marshall Cline. In particular points 1-6.
It will explain how to correctly deal with IO, how to validate input, and why you shouldn't count on "while(!in.eof())". And it always makes for excellent reading.
I'm looking for the exact rules according to standard of casting an integer type of signed type to an unsigned integer of the same rank, as well as the contrary (unsigned to signed).
Ah, that just means some quoting then
Originally Posted by C++03 Section 4.7 Paragraphs 1 to 3
An rvalue of an integer type can be converted to an rvalue of another integer type. An rvalue of an enumeration type can be converted to an rvalue of an integer type.
If the destination type is unsigned, the resulting value is the least unsigned integer congruent to the source integer (modulo 2**n where n is the number of bits used to represent the unsigned type). [Note: In a two's complement representation, this conversion is conceptual and there is no change in the bit pattern (if there is no truncation). ]
If the destination type is signed, the value is unchanged if it can be represented in the destination type (and bit-field width); otherwise, the value is implementation-defined.
Originally Posted by monarch_dodra
Is it safe to write:
Code:
unsigned int max_value = -1;
On any architecture?
Yes. In fact, this is pretty much how the standard defines npos for std::basic_string.
Last edited by laserlight; August 3rd, 2010 at 11:08 AM.
Reason: 2n => 2**n, where ** denotes exponentiation
C + C++ Compiler: MinGW port of GCC
Build + Version Control System: SCons + Bazaar
Yes. In fact, this is pretty much how the standard defines npos for std::basic_string.
If I see it rightly the section 4.7 doesn't exactly define conversion from signed to unsigned. Or if we assume the 'otherwise' part also applies to conversions to unsigned, it therefore is "implementation-defined". Also, does the C++ standard really 'define' the implementation for std::basic_string::npos or is it a concrete implementation where it is defined by assigning -1?
I would say a static_cast will do the job regardless of the implementation. And
Code:
unsigned int ui = -1;
implicitly does a static cast but wouldn't necessarily help to get the maximum unsigned integer.
For an integer representation using not two's-complement for negatives the bit representation of -1 could be 10000000000000000000000000000001 (one sign-bit and no complement) and I doubt very much that they would cast that to 0xfffffff.
If I see it rightly the section 4.7 doesn't exactly define conversion from signed to unsigned. Or if we assume the 'otherwise' part also applies to conversions to unsigned, it therefore is "implementation-defined".
Yes, except that the standard does define what happens if the value "can be represented in the destination type".
Originally Posted by itsmeandnobodyelse
Also, does the C++ standard really 'define' the implementation for std::basic_string::npos or is it a concrete implementation where it is defined by assigning -1?
I am not sure if an implementation is permitted to differ as long as the net effect is the same, but the C++ standard does indeed define how npos should be defined, i.e., by initialising it with -1.
Originally Posted by itsmeandnobodyelse
implicitly does a static cast but wouldn't necessarily help to get the maximum unsigned integer.
For an integer representation using not two's-complement for negatives the bit representation of -1 could be 10000000000000000000000000000001 (one sign-bit and no complement) and I doubt very much that they would cast that to 0xfffffff.
You might want to read paragraph 2 of section 4.7 of the 2003 edition of the C++ standard again. In particular, the comment on two's complement representation is a good hint as to why you are mistaken.
C + C++ Compiler: MinGW port of GCC
Build + Version Control System: SCons + Bazaar
Yes, except that the standard does define what happens if the value "can be represented in the destination type".
I am not sure if an implementation is permitted to differ as long as the net effect is the same, but the C++ standard does indeed define how npos should be defined, i.e., by initialising it with -1.
You might want to read paragraph 2 of section 4.7 of the 2003 edition of the C++ standard again. In particular, the comment on two's complement representation is a good hint as to why you are mistaken.
You probably mean the
...
[Note: In a two's complement representation, this conversion is conceptual and there is no change in the bit pattern (if there is no truncation). ]
...
Maybe I am dump but I can't see from that hint why a non-two's complement of -1 necessarily must be casted to an unsigned integer with all bits set.
Isn't it just 'implementation-defined' ?
(Even if so, the npos also would work if it is not the highest unsigned integer).
If I see it rightly the section 4.7 doesn't exactly define conversion from signed to unsigned.
That is incorrect. The standard leaves as implementation defined conversion to a signed integer type if the value cannot be represented in the destination type. This is a consequence of paragraph 3.
Paragraph 2, on the other hand, effectively means that conversion from a signed to an unsigned integer type is performed as if two's complement is always used, with respect to the value. If two's complement is not in use, the conversion results in a change in the bit pattern, if necessary (i.e., it is not true that "a non-two's complement of -1 necessarily must be casted to an unsigned integer with all bits set").
Last edited by laserlight; August 3rd, 2010 at 01:32 PM.
C + C++ Compiler: MinGW port of GCC
Build + Version Control System: SCons + Bazaar
That is incorrect. The standard leaves as implementation defined conversion to a signed integer type if the value cannot be represented in the destination type. This is a consequence of paragraph 3.
Paragraph 2, on the other hand, effectively means that conversion from a signed to an unsigned integer type is performed as if two's complement is always used, with respect to the value. If two's complement is not in use, the conversion results in a change in the bit pattern, if necessary.
That makes sense though I can't read that assertment from the paragraph 2.
I would have said the "otherwise, the value is implementation-defined." of paragraph 3 applies also for the conversion from signed to unsigned in case of non-two's complement, cause there were two if's and only one 'otherwise'.
That makes sense though I can't read that assertment from the paragraph 2.
Perhaps you will find the corresponding paragraph from the C standard easier to understand:
Originally Posted by C99 Section 6.3.1.3 Paragraph 2
Otherwise, if the new type is unsigned, the value is converted by repeatedly adding or subtracting one more than the maximum value that can be represented in the new type until the value is in the range of the new type.
This means that if you are converting -1 to unsigned int, you compute -1 + UINT_MAX + 1 = UINT_MAX, regardless of whether one's complement, two's complement or sign and magnitude is used to represent signed integer types.
Originally Posted by itsmeandnobodyelse
I would have said the "otherwise, the value is implementation-defined." of paragraph 3 applies also for the conversion from signed to unsigned in case of non-two's complement, cause there were two if's and only one 'otherwise'.
That would not make sense though. Besides the fact that they are in two different paragraphs, the first if pertains to "the destination type is unsigned". If that otherwise pertains to the first if, then it means: if the destination type is not unsigned, then the value is implementation-defined. This obviously does not make sense, and would render the first part of paragraph 3 meaningless. Therefore, one can conclude that the otherwise must pertain to the second if, i.e., if the destination type is signed and the value cannot be represented in the destination type, then the value is implementation-defined.
C + C++ Compiler: MinGW port of GCC
Build + Version Control System: SCons + Bazaar
template<typename T>
void f(unsigned T, signed T)
{
...
}
Which doesn't work. Any way to do it, without asking the user to explicitly provide both the signed and unsigned parameter.
How can I compile-time assert that they are both the same rank, and the correct signed-ness? (without boost)
Is your question related to IO?
Read this C++ FAQ LITE article at parashift by Marshall Cline. In particular points 1-6.
It will explain how to correctly deal with IO, how to validate input, and why you shouldn't count on "while(!in.eof())". And it always makes for excellent reading.
of course, things get complicated if you want automatic type deduction ...
Good idea.
You seem to be implying there is a way to do it with C++0x/boost, what's on your mind?
Originally Posted by laserlight
Must they be of the same rank, or is it sufficient to check if they are of the same size?
I would prefer same rank, but if I can't, same size is the next best thing.
Is your question related to IO?
Read this C++ FAQ LITE article at parashift by Marshall Cline. In particular points 1-6.
It will explain how to correctly deal with IO, how to validate input, and why you shouldn't count on "while(!in.eof())". And it always makes for excellent reading.
You seem to be implying there is a way to do it with C++0x/boost, what's on your mind?
Probably the stuff from <type_traits>, to be introduced in the next version of the C++ standard library. Actually, I believe that it is already available in the TR1 extensions.
C + C++ Compiler: MinGW port of GCC
Build + Version Control System: SCons + Bazaar
ps. I just quickly tested the above with the online Comeau compiler ... so you should check it
Thanks. I'll check it out.
Originally Posted by laserlight
Probably the stuff from <type_traits>, to be introduced in the next version of the C++ standard library. Actually, I believe that it is already available in the TR1 extensions.
That's a very interesting library. Thanks.
Is your question related to IO?
Read this C++ FAQ LITE article at parashift by Marshall Cline. In particular points 1-6.
It will explain how to correctly deal with IO, how to validate input, and why you shouldn't count on "while(!in.eof())". And it always makes for excellent reading.
Bookmarks