mixing signed and unsigned

J

John Harrison

I have a problem. I want to compare an integral value, n, against three
half open ranges as follows

[-A, 0) // range 1
[0, B) // range 2
[B, C} // range 3

Each range corresponds to a different outcome and if the integral value
isn't within any of the ranges, that's a fourth outcome. So far so easy,
the problem is that n is a signed quantity and A, B, C are unsigned
quantities. Apart from this obscure corner of my code this makes perfect
sense, so I don't want to change the signedness of anything.

How to I write these tests so that my code is reasonably understandable,
rather than a horrible mess of casts and compiler warnings?

One more point, of the unsigned quantity, only B is guaranteed small
enough that it could be safely cast to a signed integer.

john
 
I

Ian Collins

John said:
I have a problem. I want to compare an integral value, n, against three
half open ranges as follows

[-A, 0) // range 1
[0, B) // range 2
[B, C} // range 3

Each range corresponds to a different outcome and if the integral value
isn't within any of the ranges, that's a fourth outcome. So far so easy,
the problem is that n is a signed quantity and A, B, C are unsigned
quantities. Apart from this obscure corner of my code this makes perfect
sense, so I don't want to change the signedness of anything.

How to I write these tests so that my code is reasonably understandable,
rather than a horrible mess of casts and compiler warnings?

One more point, of the unsigned quantity, only B is guaranteed small
enough that it could be safely cast to a signed integer.
Use the next size up, int64_t?
 
J

John Harrison

Ian said:
John said:
I have a problem. I want to compare an integral value, n, against three
half open ranges as follows

[-A, 0) // range 1
[0, B) // range 2
[B, C} // range 3

Each range corresponds to a different outcome and if the integral value
isn't within any of the ranges, that's a fourth outcome. So far so easy,
the problem is that n is a signed quantity and A, B, C are unsigned
quantities. Apart from this obscure corner of my code this makes perfect
sense, so I don't want to change the signedness of anything.

How to I write these tests so that my code is reasonably understandable,
rather than a horrible mess of casts and compiler warnings?

One more point, of the unsigned quantity, only B is guaranteed small
enough that it could be safely cast to a signed integer.

Use the next size up, int64_t?

Well a further problem is that the signed type is ptrdiff_t and the
unsigned type is size_t. So I can't say what the next size up is, or
even if it exists.

In the past I've ranted against the very existance of unsigned types on
this group. I promise I'll repent if someone can show me an elegant way
to do this.

john
 
J

John Harrison

In the past I've ranted against the very existance of unsigned types on
this group. I promise I'll repent if someone can show me an elegant way
to do this.

john

Answering my own question here, but this is the best I've come up with

if (A <= std::numeric_limits<ptrdiff_t>::max() &&
n < -static_cast<ptrdiff_t>(A))
{
// out of range
}
else if (n < 0)
{
// range 1
}
else if (n < static_cast<ptrdiff_t><(B))
{
// range 2
}
else if (C <= std::numeric_limits<ptrdiff_t>::max() &&
n < static_cast<ptrdiff_t>(C))
{
// range 3
}
else
{
// out of range
}

Not quite a bad as I feared but still pretty ugly.

john
 
J

John Harrison

John said:
Answering my own question here, but this is the best I've come up with

if (A <= std::numeric_limits<ptrdiff_t>::max() &&
n < -static_cast<ptrdiff_t>(A))
{
// out of range
}
else if (n < 0)
{
// range 1
}
else if (n < static_cast<ptrdiff_t><(B))
{
// range 2
}
else if (C <= std::numeric_limits<ptrdiff_t>::max() &&
n < static_cast<ptrdiff_t>(C))
{
// range 3
}
else
{
// out of range
}

Not quite a bad as I feared but still pretty ugly.

john

Spoke too soon, here's the corrected version with a couple more casts

if (A <= static_cast<size_t>(std::numeric_limits<ptrdiff_t>::max())
&& n < -static_cast<ptrdiff_t>(A))
{
// out of range
}
else if (n < 0)
{
// range 1
}
else if (n < static_cast<ptrdiff_t>(B))
{
// range 2
}
else if (C <= static_cast<size_t>(std::numeric_limits<ptrdiff_t>::max())
&& n < static_cast<ptrdiff_t>(C))
{
// range 3
}
else
{
// out of range
}

john
 
J

John Harrison

John said:
if (A <= static_cast<size_t>(std::numeric_limits<ptrdiff_t>::max())
&& n < -static_cast<ptrdiff_t>(A))
{
// out of range
}
else if (n < 0)
{
// range 1
}
else if (n < static_cast<ptrdiff_t>(B))
{
// range 2
}
else if (C <= static_cast<size_t>(std::numeric_limits<ptrdiff_t>::max())
&& n < static_cast<ptrdiff_t>(C))
{
// range 3
}
else
{
// out of range
}

john

Yet another correction. Just proves how horrible this signed/unsigned
stuff is, or am I missing a trick?

else if (C > static_cast<size_t>(std::numeric_limits<ptrdiff_t>::max())
|| n < static_cast<ptrdiff_t>(C))

john
 
K

Kai-Uwe Bux

John said:
I have a problem. I want to compare an integral value, n, against three
half open ranges as follows

[-A, 0) // range 1
[0, B) // range 2
[B, C} // range 3

Each range corresponds to a different outcome and if the integral value
isn't within any of the ranges, that's a fourth outcome. So far so easy,
the problem is that n is a signed quantity and A, B, C are unsigned
quantities. Apart from this obscure corner of my code this makes perfect
sense, so I don't want to change the signedness of anything.

How to I write these tests so that my code is reasonably understandable,
rather than a horrible mess of casts and compiler warnings?

One more point, of the unsigned quantity, only B is guaranteed small
enough that it could be safely cast to a signed integer.

What about:

#include <iostream>

int main ( void ) {
unsigned long A = 30;
unsigned long B = 20;
unsigned long C = 100;
long x = 0;
while ( std::cin >> x ) {
if ( x >= 0 ) {
if ( x < B ) {
std::cout << "range 2";
} else if ( x < C ) {
std::cout << "range 3";
} else {
std::cout << "above all ranges";
}
} else {
if ( ( -x ) <= A ) {
std::cout << "range 1";
} else {
std::cout << "below all ranges";
}
}
std::cout << '\n';
}
}

This only compares positive values. As long as the unsigned type is large
enough to represent the absolute values of the signed type, you will be
fine.


Best

Kai-Uwe Bux
 
P

Pete Becker

John said:
I have a problem. I want to compare an integral value, n, against three
half open ranges as follows

[-A, 0) // range 1
[0, B) // range 2
[B, C} // range 3

Each range corresponds to a different outcome and if the integral value
isn't within any of the ranges, that's a fourth outcome. So far so easy,
the problem is that n is a signed quantity and A, B, C are unsigned
quantities. Apart from this obscure corner of my code this makes perfect
sense, so I don't want to change the signedness of anything.

How to I write these tests so that my code is reasonably understandable,
rather than a horrible mess of casts and compiler warnings?

One more point, of the unsigned quantity, only B is guaranteed small
enough that it could be safely cast to a signed integer.

In that case, the values of the range limits need to be adjusted. If A
is greater than INT_MAX, replace it with INT_MAX. Same for C. Then do
everything signed.

--

-- Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com)
Author of "The Standard C++ Library Extensions: a Tutorial and
Reference." (www.petebecker.com/tr1book)
 
J

Jerry Coffin

I have a problem. I want to compare an integral value, n, against three
half open ranges as follows

[-A, 0) // range 1
[0, B) // range 2
[B, C} // range 3

Each range corresponds to a different outcome and if the integral value
isn't within any of the ranges, that's a fourth outcome. So far so easy,
the problem is that n is a signed quantity and A, B, C are unsigned
quantities. Apart from this obscure corner of my code this makes perfect
sense, so I don't want to change the signedness of anything.

How to I write these tests so that my code is reasonably understandable,
rather than a horrible mess of casts and compiler warnings?

One more point, of the unsigned quantity, only B is guaranteed small
enough that it could be safely cast to a signed integer.

If you can't count on having a type with a large enough range to hold
all the possible values, you probably need to work primarily with
unsigned values:

bool in_range_u(ptrdiff_t value, size_t min, size_t max) {
if (value < 0)
return false;
return (static_cast<size_t>(value) >= min) &&
(static_cast<size_t>(value) < max);
}

if (in_range_u(x, 0, B))
first_range();
else if (in_range_u(-x, 0, A+1)
second_range();
else if (in_range_u(x, B, C)
third_range();
else
fourth_range();

The two casts aren't really necessary, but I'd prefer them over a
warning, which would be normal for a mixed signed/unsigned comparsison.

Although I believe the code should work, it is _quite_ fragile. For
example in_range_u(-x, 0, A+1) does NOT really work correctly. We get
around that by testing the second range first, and only testing the
first range if we've determined that x!=0.

To be pedantically correct, you might need to add special case code for
a couple of possibilities: one is for magnitude(A) == magnitude
(INT_MIN), and the other is for A==std::numeric_limits<size_t>::max. It
isn't hard to do these without warnings or casts, but it does make the
code a bit ugly.
 
J

John Harrison

Kai-Uwe Bux said:
John Harrison wrote:

I have a problem. I want to compare an integral value, n, against three
half open ranges as follows

[-A, 0) // range 1
[0, B) // range 2
[B, C} // range 3

Each range corresponds to a different outcome and if the integral value
isn't within any of the ranges, that's a fourth outcome. So far so easy,
the problem is that n is a signed quantity and A, B, C are unsigned
quantities. Apart from this obscure corner of my code this makes perfect
sense, so I don't want to change the signedness of anything.

How to I write these tests so that my code is reasonably understandable,
rather than a horrible mess of casts and compiler warnings?

One more point, of the unsigned quantity, only B is guaranteed small
enough that it could be safely cast to a signed integer.


What about:

#include <iostream>

int main ( void ) {
unsigned long A = 30;
unsigned long B = 20;
unsigned long C = 100;
long x = 0;
while ( std::cin >> x ) {
if ( x >= 0 ) {
if ( x < B ) {
std::cout << "range 2";
} else if ( x < C ) {
std::cout << "range 3";
} else {
std::cout << "above all ranges";
}
} else {
if ( ( -x ) <= A ) {
std::cout << "range 1";
} else {
std::cout << "below all ranges";
}
}
std::cout << '\n';
}
}

This only compares positive values. As long as the unsigned type is large
enough to represent the absolute values of the signed type, you will be
fine.

I thought of something like that, but two problems.

-x would overflow if x == std::numeric_limits<ptrdiff_t>::min()

"above all" and "below all" are the same outcome and I'd prefer not to
write the same code twice

john
 
J

John Harrison

Pete said:
John said:
I have a problem. I want to compare an integral value, n, against
three half open ranges as follows

[-A, 0) // range 1
[0, B) // range 2
[B, C} // range 3

Each range corresponds to a different outcome and if the integral
value isn't within any of the ranges, that's a fourth outcome. So far
so easy, the problem is that n is a signed quantity and A, B, C are
unsigned quantities. Apart from this obscure corner of my code this
makes perfect sense, so I don't want to change the signedness of
anything.

How to I write these tests so that my code is reasonably
understandable, rather than a horrible mess of casts and compiler
warnings?

One more point, of the unsigned quantity, only B is guaranteed small
enough that it could be safely cast to a signed integer.

In that case, the values of the range limits need to be adjusted. If A
is greater than INT_MAX, replace it with INT_MAX. Same for C. Then do
everything signed.

If A equals INT_MAX + 1, then -A == INT_MIN so I have to deal with that
(unlikely) case too.

john
 
J

John Harrison

Jerry said:
I have a problem. I want to compare an integral value, n, against three
half open ranges as follows

[-A, 0) // range 1
[0, B) // range 2
[B, C} // range 3

Each range corresponds to a different outcome and if the integral value
isn't within any of the ranges, that's a fourth outcome. So far so easy,
the problem is that n is a signed quantity and A, B, C are unsigned
quantities. Apart from this obscure corner of my code this makes perfect
sense, so I don't want to change the signedness of anything.

How to I write these tests so that my code is reasonably understandable,
rather than a horrible mess of casts and compiler warnings?

One more point, of the unsigned quantity, only B is guaranteed small
enough that it could be safely cast to a signed integer.


If you can't count on having a type with a large enough range to hold
all the possible values, you probably need to work primarily with
unsigned values:

bool in_range_u(ptrdiff_t value, size_t min, size_t max) {
if (value < 0)
return false;
return (static_cast<size_t>(value) >= min) &&
(static_cast<size_t>(value) < max);
}

if (in_range_u(x, 0, B))
first_range();
else if (in_range_u(-x, 0, A+1)
second_range();
else if (in_range_u(x, B, C)
third_range();
else
fourth_range();

The two casts aren't really necessary, but I'd prefer them over a
warning, which would be normal for a mixed signed/unsigned comparsison.

Although I believe the code should work, it is _quite_ fragile. For
example in_range_u(-x, 0, A+1) does NOT really work correctly. We get
around that by testing the second range first, and only testing the
first range if we've determined that x!=0.

To be pedantically correct, you might need to add special case code for
a couple of possibilities: one is for magnitude(A) == magnitude
(INT_MIN), and the other is for A==std::numeric_limits<size_t>::max. It
isn't hard to do these without warnings or casts, but it does make the
code a bit ugly.

Pedantically correct is what I'm aiming for. Here's my latest effort, it
took me several goes but I believe its correct.

static size_t MAX_A = std::numeric_limits<ptrdiff_t>::max() + 1;
static size_t MAX_C = std::numeric_limits<ptrdiff_t>::max();
if (A <= MAX_A && n < static_cast<ptrdiff_t>(-A) ||
C <= MAX_C && n >= static_cast<ptrdiff_t>(C))
{
}
else if (n < 0)
{
}
else if (n < static_cast<ptrdiff_t>(B))
{
}
else
{
}

It does produce one stupid warning on my compiler 'unary minus operator
applied to unsigned type, result still unsigned' but I can turn that off.

john
 
K

Kai-Uwe Bux

John said:
Kai-Uwe Bux said:
John Harrison wrote:

I have a problem. I want to compare an integral value, n, against three
half open ranges as follows

[-A, 0) // range 1
[0, B) // range 2
[B, C} // range 3

Each range corresponds to a different outcome and if the integral value
isn't within any of the ranges, that's a fourth outcome. So far so easy,
the problem is that n is a signed quantity and A, B, C are unsigned
quantities. Apart from this obscure corner of my code this makes perfect
sense, so I don't want to change the signedness of anything.

How to I write these tests so that my code is reasonably understandable,
rather than a horrible mess of casts and compiler warnings?

One more point, of the unsigned quantity, only B is guaranteed small
enough that it could be safely cast to a signed integer.


What about:

#include <iostream>

int main ( void ) {
unsigned long A = 30;
unsigned long B = 20;
unsigned long C = 100;
long x = 0;
while ( std::cin >> x ) {
if ( x >= 0 ) {
if ( x < B ) {
std::cout << "range 2";
} else if ( x < C ) {
std::cout << "range 3";
} else {
std::cout << "above all ranges";
}
} else {
if ( ( -x ) <= A ) {
std::cout << "range 1";
} else {
std::cout << "below all ranges";
}
}
std::cout << '\n';
}
}

This only compares positive values. As long as the unsigned type is large
enough to represent the absolute values of the signed type, you will be
fine.

I thought of something like that, but two problems.

-x would overflow if x == std::numeric_limits<ptrdiff_t>::min()

Oops, you'r right. I forgot a cast.

"above all" and "below all" are the same outcome and I'd prefer not to
write the same code twice

The two cases are, of course, not the same. You just want them to be the
same. In that case, just use a goto :)


So, the corrected code is:

#include <iostream>
#include <limits>

int main ( void ) {
unsigned long A = 30;
unsigned long B = 20;
unsigned long C = 100;
int x = 0;
while ( std::cin >> x ) {
if ( x >= 0 ) {
if ( x < B ) {
std::cout << "range 2";
} else if ( x < C ) {
std::cout << "range 3";
} else {
out_of_range:
std::cout << "out_of_range";
}
} else {
// we cast first so that unary - does not cause an overflow.
if ( -static_cast<unsigned long>(x) <= A ) {
std::cout << "range 1";
} else {
// we do not distinguish underflow and overflow:
goto out_of_range;
}
}
std::cout << '\n';
}
}


Of course, there are ways to get around the goto, e.g., you could put the
common code into a function.


Best

Kai-Uwe Bux
 
K

Kai-Uwe Bux

John said:
Jerry said:
I have a problem. I want to compare an integral value, n, against three
half open ranges as follows

[-A, 0) // range 1
[0, B) // range 2
[B, C} // range 3

Each range corresponds to a different outcome and if the integral value
isn't within any of the ranges, that's a fourth outcome. So far so easy,
the problem is that n is a signed quantity and A, B, C are unsigned
quantities. Apart from this obscure corner of my code this makes perfect
sense, so I don't want to change the signedness of anything.

How to I write these tests so that my code is reasonably understandable,
rather than a horrible mess of casts and compiler warnings?

One more point, of the unsigned quantity, only B is guaranteed small
enough that it could be safely cast to a signed integer.


If you can't count on having a type with a large enough range to hold
all the possible values, you probably need to work primarily with
unsigned values:

bool in_range_u(ptrdiff_t value, size_t min, size_t max) {
if (value < 0)
return false;
return (static_cast<size_t>(value) >= min) &&
(static_cast<size_t>(value) < max);
}

if (in_range_u(x, 0, B))
first_range();
else if (in_range_u(-x, 0, A+1)
second_range();
else if (in_range_u(x, B, C)
third_range();
else
fourth_range();

The two casts aren't really necessary, but I'd prefer them over a
warning, which would be normal for a mixed signed/unsigned comparsison.

Although I believe the code should work, it is _quite_ fragile. For
example in_range_u(-x, 0, A+1) does NOT really work correctly. We get
around that by testing the second range first, and only testing the
first range if we've determined that x!=0.

To be pedantically correct, you might need to add special case code for
a couple of possibilities: one is for magnitude(A) == magnitude
(INT_MIN), and the other is for A==std::numeric_limits<size_t>::max. It
isn't hard to do these without warnings or casts, but it does make the
code a bit ugly.

Pedantically correct is what I'm aiming for. Here's my latest effort, it
took me several goes but I believe its correct.

static size_t MAX_A = std::numeric_limits<ptrdiff_t>::max() + 1;

That looks like undefined behavior: the RHS computes a signed integer
overflow _before_ it is cast to unsigned.

Ok, this depends on whether 1 is signed or unsigned. I think the type of 1
is int, not unsigned. Let's test this:

#include <iostream>
#include <string>

std::string type ( int ) {
return ( "int" );
}

std::string type ( unsigned ) {
return ( "unsigned" );
}

int main ( void ) {
std::cout << type(1) << '\n';
}

And the winner is:

news_group> a.out
int

static size_t MAX_C = std::numeric_limits<ptrdiff_t>::max();
if (A <= MAX_A && n < static_cast<ptrdiff_t>(-A) ||

This, too, looks fishy. What if A == numeric_limits<ptrdiff_t>::max()?
Assuming that ptrdiff_t and size_t have the same number of bits, this value
would be the middle of size_t and it would be a fixed point w.r.t. unary -.
I.e., -A == A. Then, the static_cast will not change the value to the
C <= MAX_C && n >= static_cast<ptrdiff_t>(C))
{
}
else if (n < 0)
{
}
else if (n < static_cast<ptrdiff_t>(B))
{
}
else
{
}

It does produce one stupid warning on my compiler 'unary minus operator
applied to unsigned type, result still unsigned' but I can turn that off.


Best

Kai-Uwe Bux
 
J

John Carson

John Harrison said:
Kai-Uwe Bux said:
John Harrison wrote:

I have a problem. I want to compare an integral value, n, against
three half open ranges as follows

[-A, 0) // range 1
[0, B) // range 2
[B, C} // range 3

Each range corresponds to a different outcome and if the integral
value isn't within any of the ranges, that's a fourth outcome. So
far so easy, the problem is that n is a signed quantity and A, B, C
are unsigned quantities. Apart from this obscure corner of my code
this makes perfect sense, so I don't want to change the signedness
of anything. How to I write these tests so that my code is reasonably
understandable, rather than a horrible mess of casts and compiler
warnings? One more point, of the unsigned quantity, only B is guaranteed
small
enough that it could be safely cast to a signed integer.


What about:

#include <iostream>

int main ( void ) {
unsigned long A = 30;
unsigned long B = 20;
unsigned long C = 100;
long x = 0;
while ( std::cin >> x ) {
if ( x >= 0 ) {
if ( x < B ) {
std::cout << "range 2";
} else if ( x < C ) {
std::cout << "range 3";
} else {
std::cout << "above all ranges";
}
} else {
if ( ( -x ) <= A ) {
std::cout << "range 1";
} else {
std::cout << "below all ranges";
}
}
std::cout << '\n';
}
}

This only compares positive values. As long as the unsigned type is
large enough to represent the absolute values of the signed type,
you will be fine.

I thought of something like that, but two problems.

-x would overflow if x == std::numeric_limits<ptrdiff_t>::min()

How about -(x+1) <= A-1? Executing this conditional on x being negative
looks OK to me.
 
J

John Harrison

Kai-Uwe Bux said:
That looks like undefined behavior: the RHS computes a signed integer
overflow _before_ it is cast to unsigned.

Yes you're right, yet another cast will fix that.
This, too, looks fishy. What if A == numeric_limits<ptrdiff_t>::max()?
Assuming that ptrdiff_t and size_t have the same number of bits, this value
would be the middle of size_t and it would be a fixed point w.r.t. unary -.
I.e., -A == A. Then, the static_cast will not change the value to the
negative side. If n == -numeric_limits<ptrdiff_t>::max(), you get the wrong
answer.

Doe you mean A == numeric_limits<ptrdiff_t>::max() + 1 ? Then A is at
the fixed point but in that case the static_cast does change the value
to the negative side.

size_t x = static_cast<size_t>(std::numeric_limits<ptrdiff_t>::max()) + 1;
std::cout << x << '\n';
std::cout << -x << '\n';
std::cout << static_cast<ptrdiff_t>(-x) << '\n';

output (on my platform)

2147483648
2147483648
-2147483648

john
 
K

Kai-Uwe Bux

John said:
John Harrison said:
Kai-Uwe Bux said:
John Harrison wrote:


I have a problem. I want to compare an integral value, n, against
three half open ranges as follows

[-A, 0) // range 1
[0, B) // range 2
[B, C} // range 3

Each range corresponds to a different outcome and if the integral
value isn't within any of the ranges, that's a fourth outcome. So
far so easy, the problem is that n is a signed quantity and A, B, C
are unsigned quantities. Apart from this obscure corner of my code
this makes perfect sense, so I don't want to change the signedness
of anything. How to I write these tests so that my code is reasonably
understandable, rather than a horrible mess of casts and compiler
warnings? One more point, of the unsigned quantity, only B is
guaranteed small
enough that it could be safely cast to a signed integer.


What about:

#include <iostream>

int main ( void ) {
unsigned long A = 30;
unsigned long B = 20;
unsigned long C = 100;
long x = 0;
while ( std::cin >> x ) {
if ( x >= 0 ) {
if ( x < B ) {
std::cout << "range 2";
} else if ( x < C ) {
std::cout << "range 3";
} else {
std::cout << "above all ranges";
}
} else {
if ( ( -x ) <= A ) {
std::cout << "range 1";
} else {
std::cout << "below all ranges";
}
}
std::cout << '\n';
}
}

This only compares positive values. As long as the unsigned type is
large enough to represent the absolute values of the signed type,
you will be fine.

I thought of something like that, but two problems.

-x would overflow if x == std::numeric_limits<ptrdiff_t>::min()

How about -(x+1) <= A-1? Executing this conditional on x being negative
looks OK to me.

Runs into trouble for A == 0: it would accept everything where it should
reject everything; A is unsigned, if A==0 then A-1 is _very_ large.


Best

Kai-Uwe Bux
 
K

Kai-Uwe Bux

John said:
Kai-Uwe Bux wrote: [snip]
This, too, looks fishy. What if A == numeric_limits<ptrdiff_t>::max()?
Assuming that ptrdiff_t and size_t have the same number of bits, this
value would be the middle of size_t and it would be a fixed point w.r.t.
unary -. I.e., -A == A. Then, the static_cast will not change the value
to the negative side. If n == -numeric_limits<ptrdiff_t>::max(), you get
the wrong answer.

Doe you mean A == numeric_limits<ptrdiff_t>::max() + 1 ? Then A is at
the fixed point but in that case the static_cast does change the value
to the negative side.

size_t x = static_cast<size_t>(std::numeric_limits<ptrdiff_t>::max()) + 1;
std::cout << x << '\n';
std::cout << -x << '\n';
std::cout << static_cast<ptrdiff_t>(-x) << '\n';

output (on my platform)

2147483648
2147483648
-2147483648
 
K

Kai-Uwe Bux

Sorry for the missing body in the previous attempt to post. My head starts
spinning :)

John said:
Kai-Uwe Bux wrote: [snip]
This, too, looks fishy. What if A == numeric_limits<ptrdiff_t>::max()?
Assuming that ptrdiff_t and size_t have the same number of bits, this
value would be the middle of size_t and it would be a fixed point w.r.t.
unary -. I.e., -A == A. Then, the static_cast will not change the value
to the negative side. If n == -numeric_limits<ptrdiff_t>::max(), you get
the wrong answer.

Doe you mean A == numeric_limits<ptrdiff_t>::max() + 1 ?

You are right. I did not get the fixed point right.
Then A is at
the fixed point but in that case the static_cast does change the value
to the negative side.

Why? See [4.7./3] which tells you that the result of this conversion is
implementation defined.
size_t x = static_cast<size_t>(std::numeric_limits<ptrdiff_t>::max()) + 1;
std::cout << x << '\n';
std::cout << -x << '\n';
std::cout << static_cast<ptrdiff_t>(-x) << '\n';

output (on my platform)

2147483648
2147483648
-2147483648

Ok, let me think this through carefully. I just feel in my bones that
something is wrong with the line

if (A <= MAX_A && n < static_cast<ptrdiff_t>(-A) ||

as a means of checking whether n below [A,0).


We know A is unsigned and

A <= MAX_A

where MAX_A == 1+static_cast<size_t>(std::numeric_limits<ptrdiff_t>::max())

i.e.

A in [0,MAX_A]

Thus -A in [2^N-1,2^N-MAX_A] or -A == 0.

Now, what happens if we cast -A to the signed type? For values of -A above
numeric_limits<ptrdiff_t> (and that is almost all values that occur here),
we have an implementation defined value [4.7/3].

This is probably bad.


Best

Kai-Uwe Bux
 
J

John Carson

Kai-Uwe Bux said:
John said:
John Harrison said:
Kai-Uwe Bux wrote:
John Harrison wrote:


I have a problem. I want to compare an integral value, n, against
three half open ranges as follows

[-A, 0) // range 1
[0, B) // range 2
[B, C} // range 3

Each range corresponds to a different outcome and if the integral
value isn't within any of the ranges, that's a fourth outcome. So
far so easy, the problem is that n is a signed quantity and A, B,
C are unsigned quantities. Apart from this obscure corner of my
code this makes perfect sense, so I don't want to change the
signedness of anything. How to I write these tests so that my
code is reasonably understandable, rather than a horrible mess of
casts and compiler warnings? One more point, of the unsigned
quantity, only B is guaranteed small
enough that it could be safely cast to a signed integer.


What about:

#include <iostream>

int main ( void ) {
unsigned long A = 30;
unsigned long B = 20;
unsigned long C = 100;
long x = 0;
while ( std::cin >> x ) {
if ( x >= 0 ) {
if ( x < B ) {
std::cout << "range 2";
} else if ( x < C ) {
std::cout << "range 3";
} else {
std::cout << "above all ranges";
}
} else {
if ( ( -x ) <= A ) {
std::cout << "range 1";
} else {
std::cout << "below all ranges";
}
}
std::cout << '\n';
}
}

This only compares positive values. As long as the unsigned type is
large enough to represent the absolute values of the signed type,
you will be fine.


I thought of something like that, but two problems.

-x would overflow if x == std::numeric_limits<ptrdiff_t>::min()

How about -(x+1) <= A-1? Executing this conditional on x being
negative looks OK to me.

Runs into trouble for A == 0: it would accept everything where it
should reject everything; A is unsigned, if A==0 then A-1 is _very_
large.

I was assuming A is positive, i.e., that all intervals are non-empty. If
that is not the case, then a separate check for A > 0 would deal with the
problem.

[for x<0]

if (A>0 && -(x+1) <= A-1)
std::cout << "range 1";
 

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,755
Messages
2,569,536
Members
45,011
Latest member
AjaUqq1950

Latest Threads

Top