static_cast<unsigned short)( -1 ) Well defined?

J

Jim Langston

Is the following well defined?

size_t IntVal = 65537;
unsigned short Length;

if ( IntVal > static_cast<unsigned short>( -1 ) )
{
std::cout << "Value too long to fit in a short" << std::endl;
}
else
{
std::cout << "Value fits" << std::endl;
Length = static_cast<unsigned short>( IntVal );
}

What I'm actually going to be using it for is to send a length short through
sockets, and I want to ensure that the length isn't greater than 65535.
Rather than the magic number, I was thinking that static_cast<unsigned
short>( -1 ) would be better. Is this well defined? Will it equal 65535 in
all cases where a short is a 2 byte integer?
 
K

Kai-Uwe Bux

Jim said:
Is the following well defined?

size_t IntVal = 65537;
unsigned short Length;

if ( IntVal > static_cast<unsigned short>( -1 ) )
{
std::cout << "Value too long to fit in a short" << std::endl;
}
else
{
std::cout << "Value fits" << std::endl;
Length = static_cast<unsigned short>( IntVal );
}

What I'm actually going to be using it for is to send a length short
through sockets, and I want to ensure that the length isn't greater than
65535.

If that is want you want to check, you should say so:

if ( IntVal > 65535 ) { ...

Rather than the magic number, I was thinking that
static_cast<unsigned short>( -1 ) would be better.

Why? If you want to check > 65535, then presumably, your specs contain a
magic number. Your code should reflect that.
Is this well defined?

Your code has implementation defined behavior. Moreover, it is guaranteed,
that for IntVal <= 65535, the "Value fits" branch will be taken.
Will it equal 65535 in all cases where a short is a 2 byte integer?

Yes, provided your bytes have eight bits. However, your bytes maybe longer
and your shorts may have more than 2 bytes.


Now, you need to distinguish whether you want to check

(a) whether IntVal <= 65536, or
(b) whether IntVal can be faithfully represented as a short unsigned.

Your code does the later, and as far as I can see, it does so correctly.


Best

Kai-Uwe Bux
 
S

Salt_Peter

Jim said:
Is the following well defined?

size_t IntVal = 65537;
unsigned short Length;

if ( IntVal > static_cast<unsigned short>( -1 ) )
{
std::cout << "Value too long to fit in a short" << std::endl;
}
else
{
std::cout << "Value fits" << std::endl;
Length = static_cast<unsigned short>( IntVal );
}

What I'm actually going to be using it for is to send a length short through
sockets, and I want to ensure that the length isn't greater than 65535.
Rather than the magic number, I was thinking that static_cast<unsigned
short>( -1 ) would be better. Is this well defined? Will it equal 65535 in
all cases where a short is a 2 byte integer?

I doubt that can be enforced or guarenteed other than by checking
dutifully- but i may be wrong.
take a look at:
template<>
struct numeric_limits<unsigned short>
in...
#include <limits>

namespace Project {
typedef unsigned short usocket;
};

int main()
{
using Project::usocket;
using std::numeric_limits;
std::cout << "numeric_limits< usocket >::min() ";
std::cout << numeric_limits< usocket >::min();
std::cout << std::endl;
std::cout << "numeric_limits< usocket >::max() ";
std::cout << numeric_limits< usocket >::max();
std::cout << std::endl;

int n(65530);
for(size_t t = 0; t < 10; ++t)
{
std::cout << "n++ = " << n++;
usocket usock(static_cast<usocket>(n));
std::cout << "\tusock = " << usock;
std::cout << std::endl;
}
}

/*
numeric_limits< usocket >::min() 0
numeric_limits< usocket >::max() 65535
n++ = 65530 usock = 65531
n++ = 65531 usock = 65532
n++ = 65532 usock = 65533
n++ = 65533 usock = 65534
n++ = 65534 usock = 65535
n++ = 65535 usock = 0
n++ = 65536 usock = 1
n++ = 65537 usock = 2
n++ = 65538 usock = 3
n++ = 65539 usock = 4
*/
 
F

Frederick Gotham

Jim Langston:
Is the following well defined?

size_t IntVal = 65537;


Slight problem here.

The range guaranteed for size_t is:

0 through 65535

On a system which has:

unsigned int : 16-Bit
unsigned long : 32-Bit
size_t : 16-Bit

, then this definition would be interpreted as:

size_t IntVal = 65537LU;

When converting to "size_t" the overflow would wrap around, making the
definition equivalent to:

size_t IntVal = 2;

unsigned short Length;

if ( IntVal > static_cast<unsigned short>( -1 ) )


If you convert -1 to a unsigned integer type, then you're left with the
maximum value for that unsigned integer type. E.g.

char unsigned i = -1; char unsigned i = UCHAR_MAX;

short unsigned i = -1; short unsigned i = USHRT_MAX;

and so on...

Before the comparison takes place, the unsigned short value will be either
promoted to "int" or "unsigned int". Next, the types of the two operands will
be matched, possible yielding something like:

if ( (long unsigned)IntVal > (long unsigned)USHRT_MAX )

{
std::cout << "Value too long to fit in a short" << std::endl;
}
else
{
std::cout << "Value fits" << std::endl;
Length = static_cast<unsigned short>( IntVal );
}

What I'm actually going to be using it for is to send a length short
through sockets, and I want to ensure that the length isn't greater than
65535. Rather than the magic number, I was thinking that
static_cast<unsigned short>( -1 ) would be better. Is this well
defined? Will it equal 65535 in all cases where a short is a 2 byte
integer?


To be honest, I'd have to read through the code in detail to see exactly what
you're trying to do... things to watch out for though are:

(1) Integer promotion (specifically, the possibility of "unsigned short"
promoting to "unsigned int" rather than "signed int").
(2) The range of "size_t" -- I'm not sure, but I think you could conceivably
have a system where:

typedef char unsigned size_t;

Of course, on such a system, a byte would have to be at least 16-Bit, but I
think you might want to watch out for:

sizeof(short unsigned) > sizeof(size_t)

Of course, sizeof doesn't tell us how many value representation bits an
integer type has. To determine this, we can use the C-Style macro entitled
"IMAX_BITS" (Google for it), or we can use numeric_limits.
 
J

Jim Langston

Frederick Gotham said:
Jim Langston:



Slight problem here.

The range guaranteed for size_t is:

0 through 65535

On a system which has:

unsigned int : 16-Bit
unsigned long : 32-Bit
size_t : 16-Bit

, then this definition would be interpreted as:

size_t IntVal = 65537LU;

On my compiler size_t is defined thusly:

#ifndef _SIZE_T_DEFINED
#ifdef _WIN64
typedef unsigned __int64 size_t;
#else
typedef _W64 unsigned int size_t;
#endif
#define _SIZE_T_DEFINED
#endif

Basically an int. I'm not sure how it's defined on the 64 bit platform
(things that suggest they would go to 64 bit with the _W64 didnt'
necessarily).
 
F

Frederick Gotham

Jim Langston:
On my compiler size_t is defined thusly:

#ifndef _SIZE_T_DEFINED
#ifdef _WIN64
typedef unsigned __int64 size_t;
#else
typedef _W64 unsigned int size_t;
#endif
#define _SIZE_T_DEFINED
#endif

Basically an int. I'm not sure how it's defined on the 64 bit platform
(things that suggest they would go to 64 bit with the _W64 didnt'
necessarily).


I've no doubt that the code might work on your platform, or even on the
majority of platforms. And I've not doubt that your compiler documentation
might even define the behaviour of the code.

All I can tell you is that the behaviour of the snippet is not defined by the
C++ Standard.
 
J

Jack Klein

Jim Langston:



Slight problem here.

The range guaranteed for size_t is:

0 through 65535

On a system which has:

unsigned int : 16-Bit
unsigned long : 32-Bit
size_t : 16-Bit

, then this definition would be interpreted as:

size_t IntVal = 65537LU;

No, you're quite incorrect. Check the C++ standard. An unsuffixed
decimal literal never has an unsigned type. On a platform where int
has 16 bits:

size_t IntVal = 65537;

....is exactly equivalent to:

size_t IntVal = 65537L;
 
B

Bo Persson

Frederick said:
Jim Langston:



I've no doubt that the code might work on your platform, or even on
the majority of platforms. And I've not doubt that your compiler
documentation might even define the behaviour of the code.

It does, even in 64 bit mode.
All I can tell you is that the behaviour of the snippet is not
defined by the C++ Standard.

We don't know that, unless we have checked all other defines used by the
particular implementation. With the right options set, _W64 is #define'd to
nothing. Then, if __int64 is #define'd as long, and long is big enough, it
could be correct.


Bo Persson
 
F

Frederick Gotham

Bo Persson:
It does, even in 64 bit mode.


This is comp.lang.c++. Here's a list of newsgroups that this newsgroup is
not:

comp.lang.c++.mswindows.64
comp.lang.c++.playstation2
comp.lang.c++.whatever.implementation.you.can.find

I couldn't care less whether this code works on your interterrestial
spaceship, all I'm saying is that its behaviour is not defined by the C++
Standard. Here's another example of code I don't care about:

int main()
{
int i = 65535;

++i;
}

Will this program crash? That depends on the implementation. In the context
of portable code, the behaviour is undefined.

If you want to write platform-specific code which makes presumptions which
aren't necessitated by the Standard, then find another newsgroup.

We don't know that, unless we have checked all other defines used by the
particular implementation.


We know that it's not portable.

With the right options set, _W64 is #define'd
to nothing. Then, if __int64 is #define'd as long, and long is big
enough, it could be correct.


The behaviour of the code is implementation defined. Not only that though,
the implementation is free to leave the behaviour as undefined (e.g. in the
case of signed integer overflow).
 
B

Bo Persson

Frederick said:
Bo Persson:



This is comp.lang.c++. Here's a list of newsgroups that this
newsgroup is not:

comp.lang.c++.mswindows.64
comp.lang.c++.playstation2
comp.lang.c++.whatever.implementation.you.can.find

I couldn't care less whether this code works on your interterrestial
spaceship, all I'm saying is that its behaviour is not defined by
the C++ Standard. Here's another example of code I don't care about:

int main()
{
int i = 65535;

++i;
}

Will this program crash? That depends on the implementation. In the
context of portable code, the behaviour is undefined.

If you want to write platform-specific code which makes
presumptions which aren't necessitated by the Standard, then find
another newsgroup.

I hang around in other newsgroups too. Just thought you wanted it confirmed
that you were right.
We know that it's not portable.

It wasn't supposed to be portable, it is a part of one specific standard
library implementation. Whether it actually is standard conformant must be
on-topic here.
The behaviour of the code is implementation defined. Not only that
though, the implementation is free to leave the behaviour as
undefined (e.g. in the case of signed integer overflow).

We can't tell just from the snippet whether it is conformant or not. It
depends on the definitions of the names _W64 and __int64, reserved to the
implementation. With the proper definitions, it just could be.


Bo Persson
 
F

Frederick Gotham

Bo Persson:
We can't tell just from the snippet whether it is conformant or not. It
depends on the definitions of the names _W64 and __int64, reserved to
the implementation. With the proper definitions, it just could be.


It's implementation-defined as to whether the behaviour of the code is
undefined.

If you're interested, I started a thread over on comp.lang.c:

http://groups.google.ie/group/comp.lang.c/browse_thread/thread/f2bcf2bb366de4
4e/2156215b21253042?lnk=st&q=&rnum=1&hl=en#2156215b21253042

(I posted on comp.lang.c rather than comp.lang.c++ as one tends to get better
answers to these kinds of questions there.)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top