1

Probably not, but I can't think of a good solution. I'm no expert in C++ yet.

Recently I've converted a lot of ints to unsigned ints in a project. Basically everything that should never be negative is made unsigned. This removed a lot of these warnings by MinGW:

warning: comparison between signed and unsigned integer expressions [-Wsign-compare]

I love it. It makes the program more robust and the code more descriptive. However, there is one place where they still occur. It looks like this:

unsigned int subroutine_point_size = settings->get<unsigned int>("subroutine_point_size");
...
for(int dx = -subroutine_point_size;dx <= subroutine_point_size;dx++) //Fill pixels in the point's radius.
{
    for(int dy = -subroutine_point_size;dy <= subroutine_point_size;dy++)
    {
        //Do something with dx and dy here.
    }
}

In this case I can't make dx and dy unsigned. They start out negative and depend on comparing which is lesser or greater.

I don't like to make subroutine_point_size signed either, though this is the lesser evil. It indicates a size of a kernel in a pass over an image, and the kernel size can't be negative (it's probably unwise for a user ever to set this kernel size to anything more than 100 but the settings file allows for numbers up to 2^32 - 1).

So it seems there is no way to cast any of the variables to fix this. Is there a way to get rid of this warning and solve this neatly?

We're using C++11, compiling with GCC for Windows, Mac and various Unix distributions.

Ghostkeeper
  • 2,830
  • 1
  • 15
  • 27
  • Re "it makes the program more robust", that sounds like trolling. You can't really mean that. Makes the warnings go away, like casts make many warnings go away. Re "and the code more descriptive", just use a `typedef` or C++11 `using` to *define a descriptive name*. That's what the name definition functionality is there for.. – Cheers and hth. - Alf Jun 12 '15 at 09:21
  • if `subroutine_point_size` is allowed to hold a value of 2^32 - 1, then such value (or its negative) cannot be stored in a 32 bit (signed) `int`. That is the exact case why these warnings exist. You'll have to either use a wider signed integer type, or limit the range of `subroutine_point_size` and use a cast. – Sander De Dycker Jun 12 '15 at 10:01

4 Answers4

2

Cast the variables to a long int or long long int type giving at the same time the range of unsigned int (0..2^32-1) and sign.

Aki Suihkonen
  • 19,144
  • 1
  • 36
  • 57
2

You're making a big mistake.

Basically you like the name "unsigned" and you intend it to mean "not negative" but this is not what is the semantic associated to the type.

Consider the statement:

adding a signed integer and an unsigned integer the result is unsigned

Clearly it makes no sense if you consider the term "unsigned" as "not negative", yet this is what the language does: adding -3 to the unsigned value 2 you will get a huge nonsense number instead of the correct answer -1.

Indeed the choice of using an unsigned type for the size of containers is a design mistake of C++, a mistake that is too late to fix now because of backward compatibility. By the way the reason it happened has nothing to do with "non-negativeness", but just with the ability to use the 16th bit when computers were that small (i.e. being able to use 65535 elements instead of 32767). Even back then I don't think the price of wrong semantic was worth the gain (if 32767 is not enough now then 65535 won't be enough quite soon anyway).

Do not repeat the same mistake in your programs... the name is irrelevant, what counts is the semantic and for unsigned in C and C++ it is "member of the Zn modulo ring with n=2k ".

You don't want the size of a container to be the member of a modulo ring. Do you?

6502
  • 112,025
  • 15
  • 165
  • 265
  • 1
    Of course, once you start subtracting numbers from each other, they cannot be unsigned any more (unless the programmer is certain that the subtracted number is always smaller than the original). Unsigned + signed = signed, and same with multiplication. I think this is done correctly throughout the project. The use of unsigned variables mainly concerns variables such as `image_width`, the `x` and `y` coordinates in images and iterator indices `i`. There is no arithmetic on those except the `++` operator and comparisons, and these should ideally not make them negative. – Ghostkeeper Jun 12 '15 at 11:59
  • @Ghostkeeper: the problem is that `unsigned + signed = unsigned`. This is what the language does and simply means that `unsigned ` does **NOT** mean "non-negative". You're liking the name and assigning to it a different meaning from what the language means with it. Someone likes to say that using "unsigned" documents that you want non-negative numbers, but this only documents they don't know the C++ semantic. – 6502 Jun 12 '15 at 12:04
1

Instead of the current

for(int dx = -subroutine_point_size;dx <= subroutine_point_size;dx++) //Fill pixels in the point's radius.

you can do this:

for(int dx = -int(subroutine_point_size);dx <= int(subroutine_point_size);dx++) //Fill pixels in the point's radius.

where the first int cast is (1)technically redundant, but is there for consistency, because the second cast removes the signed/unsigned warning that presumably is the issue here.

However, I strongly advise you to undo the work of converting signed to unsigned types everywhere. A good rule of thumb is to use signed types for numbers, and unsigned types for bit level stuff. That avoids the problems with wrap-around due to implicit conversions, where e.g. std:.string("Bah").length() < -5 is guaranteed (very silly), and because it does away with actual problems, it also reduces spurious warnings.

Note that you can just define a suitable name, where you want to indicate that some value will never be negative.


1) Technically redundant in practice, for two's complement representation of signed integers, with no trapping inserted by the compiler. As far as I know no extant C++ compiler behaves otherwise.

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • @Kris: It's good that you recognize the casts, but what do you mean by "just". I'm sorry but your comment is completely void of meaning to me. Can you elaborate, please. – Cheers and hth. - Alf Jun 12 '15 at 09:33
  • 1
    Well to my understanding, the author of the question is aware that he can solve the compiler warning with a cast. The question is however if there is not a more elegant way to write down the piece of code, such that no casting is required. – Kris Jun 12 '15 at 09:38
  • @Kris: The OP's statement that "it seems there is no way to cast any of the variables to fix this" contradicts your supposition "the author of the question is aware that he can solve the compiler warning with a cast". Anyway, the question itself is the place to post comments that ask about intended meaning. If that's what your statement was intended to do (otherwise it's still meaningless to me, sorry). – Cheers and hth. - Alf Jun 12 '15 at 09:45
  • 2
    @Cheersandhth.-Alf : you have to also take into account the reason why the OP doesn't think casts will work (and rightly so) : `subroutine_point_size` can hold values that don't fit in a 32-bit signed `int`. In that case, your answer doesn't work. – Sander De Dycker Jun 12 '15 at 10:06
  • @SanderDeDycker: Maybe it's just me thinking of "point" as something very much less than the maximum value of a 16-bit int. (Just to be pedantic, since apparently that's where your comment is, an `int` is formally not necessarily 32 bits; it can be just 16 bits). If we start taking into account practical things, like the OP's platform, and what "point" could be, and so on, then there is no problem. – Cheers and hth. - Alf Jun 12 '15 at 10:12
  • @Cheersandhth.-Alf : My statement wasn't intended to be pedantic. 32 bits is a common width for `int`, and I wouldn't be surprised that the OP has such a system (especially since the OP used 2^32 - 1 as the reason for not considering a cast - maybe you overlooked that part of the question). – Sander De Dycker Jun 12 '15 at 10:20
  • I am aware of casts being able to solve the warning. I chose to cast `subroutine_point_size` to a `long long` to have some guarantee (at least with any sane compiler) to have at least one bit more available than `int`, which would allow it to store enough range while being signed. I will consider your strong advice though, and discuss it with a collegue. Sander hit the nail on the head in this discussion here. – Ghostkeeper Jun 12 '15 at 11:30
0

Firstly, without knowing the range of values that will be stored in the variables, your claim that changing signed to unsigned variables is unsubstantiated - there are circumstances where that claim is false.

Second, the compiler is not issuing a warning only as a result of changing variables (and I assume calls of template functions like settings.get()) to be unsigned. It is warning about the fact you have expressions involving both signed and unsigned variables. Compilers typically issue warnings about such expressions because - in practice - they are more likely to indicate a programming error or to potentially involve some behaviour that the programmer may not have anticipated (e.g. instances of undefined behaviour, expressions where a negative result is expected but a large positive result is what will occur, etc).

A rule of thumb is that, if you need to have expressions involving both signed and unsigned types, you are better off making all the relevant variables signed. While there are exceptions where that rule of thumb isn't needed, you wouldn't have asked this question if you understood how to decide that.

On that basis, I suggest the most appropriate action is to unwind your changes.

Peter
  • 35,646
  • 4
  • 32
  • 74