negative number evaluating greater than string.size()

J

Jason

Hi, below is example code which demonstrates a problem I have encountered.
When passing a number to a function I compare it with a string's size and
then take certain actions, unfortunately during the testing of it I
discovered that negative numbers were being treated as if they were > 0. I
compiled the following on mingw compiler/dev c++/windows xp.

If I replace string.size() for a ordinary number it behaves as expected? I
notice string.size() returns size type and not an int but how do I deal with
that?

Thanks in advance for any help

#include <iostream>
#include <stdlib.h>
#include <string>
using namespace std;

void something(int);

int main()
{
something(-1);
system("PAUSE");
return 0;
}

void something(int number) {
string str = "sdfjksdflksdjfkjsklfjsklfj";
if(number < str.size()) { cout << " hello" << endl; }
else { cout << " eek" << endl; }
}
 
G

Gianni Mariani

Jason said:
Hi, below is example code which demonstrates a problem I have encountered.
When passing a number to a function I compare it with a string's size and
then take certain actions, unfortunately during the testing of it I
discovered that negative numbers were being treated as if they were > 0. I
compiled the following on mingw compiler/dev c++/windows xp.

If I replace string.size() for a ordinary number it behaves as expected? I
notice string.size() returns size type and not an int but how do I deal with
that?

Thanks in advance for any help

GCC sez:

signed_probs.cpp: In function `void something(int)':
signed_probs.cpp:17: warning: comparison between signed and unsigned
integer expressions

Changing line 17 to :

if(number < static_cast<int>(str.size())) { cout << " hello" << endl; }

solves the problem.
 
J

John Harrison

Jason said:
Hi, below is example code which demonstrates a problem I have encountered.
When passing a number to a function I compare it with a string's size and
then take certain actions, unfortunately during the testing of it I
discovered that negative numbers were being treated as if they were > 0. I
compiled the following on mingw compiler/dev c++/windows xp.

If I replace string.size() for a ordinary number it behaves as expected? I
notice string.size() returns size type and not an int but how do I deal with
that?

Suggest you read the recent thread 'time to get rid of unsigned?', started
on 17/02/04.

john
 
J

John Harrison

Gianni Mariani said:
GCC sez:

signed_probs.cpp: In function `void something(int)':
signed_probs.cpp:17: warning: comparison between signed and unsigned
integer expressions

Changing line 17 to :

if(number < static_cast<int>(str.size())) { cout << " hello" << endl; }

solves the problem.

Of course this is what the OP should do, but notice that the solution
effectively rules out strings where size() > INT_MAX. So why design for such
strings in the first place? Why not have size() return an int?

john
 
L

lilburne

John said:
signed_probs.cpp: In function `void something(int)':


Of course this is what the OP should do, but notice that the solution
effectively rules out strings where size() > INT_MAX. So why design for such
strings in the first place? Why not have size() return an int?

Because its more flexible to have a type where someone can
hold a string thats over 2Gb in size. I remember something
like this being used as a justification for making size_t
unsigned some 15 years back, either in GCC documentation or
the "Standard C library", in relation to malloc().

Naturally someone will tell you that you should be coding
everything that deals with sizes and lengths with size_t.
 
J

John Harrison

lilburne said:
Because its more flexible to have a type where someone can
hold a string thats over 2Gb in size. I remember something
like this being used as a justification for making size_t
unsigned some 15 years back, either in GCC documentation or
the "Standard C library", in relation to malloc().

Interestingly the following code

int main()
{
std::string s;
std::cout << std::hex << s.max_size() << '\n';
}

when compiled with gcc 3.3.1 prints

3ffffffc

I don't think its a serious limitation to restrict yourself to strings which
occupy less than half of the available memory.
Naturally someone will tell you that you should be coding
everything that deals with sizes and lengths with size_t.

But what happens when you need to subtract one size from another? What type
should be used to hold the type of that operation? I don't think there is a
good answer.

john
 
J

John Harrison

What type should be used to hold the type of that operation?

I meant

What type should be used to hold the result of that operation?
 
R

Rolf Magnus

lilburne said:
Because its more flexible to have a type where someone can
hold a string thats over 2Gb in size.

I think it's not. You'd run into troubles if you need to calculate
offsets between two characters in your string. That offset might be
negative. Now you can't represent all possible offests anymore, no
matter whether you make the type for that offset signed or unsigned.
And further, if you mix signed and unsigned in your calculations, you
have to be very careful.
 
A

Andre Kostur

I meant

What type should be used to hold the result of that operation?

size_t, assuming the programmer has done the sane thing and checked that
they are actually subtracting the smaller from the larger.
 
A

Andrey Tarasevich

John said:
Of course this is what the OP should do, but notice that the solution
effectively rules out strings where size() > INT_MAX. So why design for such
strings in the first place? Why not have size() return an int?
...

In my opinion, using a signed type to store an unsigned quantity is a
low-level design error. For this reason, in many practical applications
signed types in C/C++ are much less useful than unsigned types. And
'size()' must return an unsigned value.

As for correcting the OP's code, I would suggest doing it differently

if (number < 0 || (std::string::size_type) number < str.size())
{ cout << " hello" << endl; }
 
L

lilburne

Rolf said:
lilburne wrote:




I think it's not.


I think so too.

You'd run into troubles if you need to calculate
offsets between two characters in your string. That offset might be
negative. Now you can't represent all possible offests anymore, no
matter whether you make the type for that offset signed or unsigned.
And further, if you mix signed and unsigned in your calculations, you
have to be very careful.

I think it was for the reasons of inadvertantly mixing
signed and unsigned that Lakos recommended avoiding unsigned
in interfaces in "Large Scale C++".
 
J

John Harrison

Andre Kostur said:
size_t, assuming the programmer has done the sane thing and checked that
they are actually subtracting the smaller from the larger.

It gets very tedious after a while.

size_t size_a = ...;
size_t size_b = ...;
size diff;
bool a_is_bigger;
if (a > b)
{
diff = size_a - size_b;
a_is_bigger = true;
}
else
{
diff = size_b - size_a;
a_is_bigger = false;
}
process_diff(diff, a_is_bigger);

and hence its prone to errors, as we have all seen. In fact I very rarely
see code that takes this much trouble. It would be easier if size_t was
defined as a signed integer, and not a great deal would have been lost.

john
 
A

Andrey Tarasevich

John said:
...
It would be easier if size_t was
defined as a signed integer, and not a great deal would have been lost.
...

It wouldn't solve anything. It would just made the problem less obvious.
In general case the difference between two signed integers does not fit
into the range of the same signed integer type. The resultant signed
type must be at least one bit longer than original types.
 
J

John Harrison

Andrey Tarasevich said:
It wouldn't solve anything. It would just made the problem less obvious.
In general case the difference between two signed integers does not fit
into the range of the same signed integer type. The resultant signed
type must be at least one bit longer than original types.

When that integer represents the size of something, it can only be positive
or zero. If it always possible to subtract two such positive signed numbers
without overflow. That's obvious isn't it?

john
 
A

Andrey Tarasevich

John said:
When that integer represents the size of something, it can only be positive
or zero. If it always possible to subtract two such positive signed numbers
without overflow. That's obvious isn't it?
...

Yes, but the trade-off in this case is that you can only use half of the
type's range.
 
J

Jeff Schwab

<reinserted>

void something(int number) {
if(number < str.size()) { /*...*/ }
}

Of course this is what the OP should do,

No, an unnecessary cast is not what the OP should do. The problem is
that a signed "number" is being compared to an unsigned "size." "int"
and "string::size_type" are not the same thing; they are not always
comparable. A more reasonable solution would be for "number" to be of
string::size_type in the first place, or of a signed type that knows how
to compare itself with string::size_type.
 
R

Rolf Magnus

lilburne said:
I think it was for the reasons of inadvertantly mixing
signed and unsigned that Lakos recommended avoiding unsigned
in interfaces in "Large Scale C++".

Same for Stroustup in TC++PL. Strange enough that size_t is unsinged,
since Stroustrup probably had to do something with it. In the book, he
writes:

"The unsigned integer types are ideal for uses that treat storage as a
bit array. Using an unsigned instead of an int to gain one more bit to
represent positive integers is almost never a good idea. Attempts to
ensure that some values are positive by declaring variables unsigned
will typically be defeated by the implicit conversion rules."
 
L

lilburne

Jeff said:
No, an unnecessary cast is not what the OP should do. The problem is
that a signed "number" is being compared to an unsigned "size." "int"
and "string::size_type" are not the same thing; they are not always
comparable. A more reasonable solution would be for "number" to be of
string::size_type in the first place, or of a signed type that knows how
to compare itself with string::size_type.

Naturally! And std::vector::size_type, std::list::size_type,
std::deque::size_type, std::set::size_type, std::map::size_type, etc,
whenever dealing with the corresponding class. Maybe they'll all be the
same underlaying type, but who can tell?
 
J

Jeff Schwab

lilburne said:
Naturally! And std::vector::size_type, std::list::size_type,
std::deque::size_type, std::set::size_type, std::map::size_type, etc,
whenever dealing with the corresponding class.

That's right.
Maybe they'll all be the
same underlaying type, but who can tell?

A specialized template can.
 
R

Rolf Magnus

Andrey said:
In my opinion, using a signed type to store an unsigned quantity is a
low-level design error.

In mine, it's a design error to use an unsigned type if you don't have a
very good reason to do so. That you only want to store non-negative
values is not such a reason.
Why is there no unsigned floating point type, just for the case you want
to store only postive values?
 

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

No members online now.

Forum statistics

Threads
473,756
Messages
2,569,535
Members
45,008
Latest member
obedient dusk

Latest Threads

Top