Today, I discovered that if you use: const unsigned int xxx = value; (which in this case happens to be = 0) and then in the code try and compare it with a signed int yyy the compiler ignores your signed int and does an unsigned compare. Up until now this hadn't been a problem as I've (quite by chance) always looked for equality with the const. But today I counted yyy down to -1 wishing to stop when yyy < xxx but the sodding compiler decided that I now had a very large positive number and refused to break from my loop. Took me flipping ages to work out what was wrong :-( The compiler does give me a warning - that I am comparing a signed with an unsigned but as I was sure that the unsigned int was the zero value const I didn't give it a second thought. But I did get rid of it by using a c-style cast to make the unsigned int a signed one when comparing, and that suppressed the warning, but it still didn't work. - not until I redefined my const as signed! I'm not hugely experienced with C++ [1], is it meant to work like this or do I have a broken compiler? We're using GCC TIA Tim [1] nor are any of my co-workers, finding good ones who will work for the pittance that embedded work now pays in my neck of the woods is tres difficult :-)
C++ problem
Started by ●March 1, 2016
Reply by ●March 1, 20162016-03-01
On Tue, 01 Mar 2016 21:02:46 +0000, tim... wrote:> Today, I discovered that if you use: > > const unsigned int xxx = value; (which in this case happens to be = 0) > > and then in the code try and compare it with a signed int yyy > > the compiler ignores your signed int and does an unsigned compare. > > Up until now this hadn't been a problem as I've (quite by chance) always > looked for equality with the const. > > But today I counted yyy down to -1 wishing to stop when yyy < xxx but > the sodding compiler decided that I now had a very large positive number > and refused to break from my loop. > > Took me flipping ages to work out what was wrong :-( > > The compiler does give me a warning - that I am comparing a signed with > an unsigned but as I was sure that the unsigned int was the zero value > const I didn't give it a second thought. > > But I did get rid of it by using a c-style cast to make the unsigned int > a signed one when comparing, and that suppressed the warning, but it > still didn't work. - not until I redefined my const as signed! > > I'm not hugely experienced with C++ [1], is it meant to work like this > or do I have a broken compiler? > > We're using GCC > > TIA > > Tim > > [1] nor are any of my co-workers, finding good ones who will work for > the pittance that embedded work now pays in my neck of the woods is tres > difficult :-)1: Yes, it's supposed to work like that. Google "automatic type conversions". 2: I'm pretty sure that C is supposed to work like that, too. If it isn't, then the behavior is implementation defined -- meaning that it won't be predictable at all. 3: "by using a c-style cast to make the unsigned int a signed one when comparing, and that suppressed the warning, but it still didn't work. - not until I redefined my const as signed!" What? 'splain, please. I thought the const was the only unsigned. You should use a C++ - style cast: if (static_cast<int>(xxx) >= yyy) { yadda yadda } 4: Use -Wall. Then, since "all" does not, somehow, mean "all" to the crew at GCC, use -Wextra, too. -- www.wescottdesign.com
Reply by ●March 1, 20162016-03-01
On 01/03/16 22:02, tim... wrote:> Today, I discovered that if you use: > > const unsigned int xxx = value; (which in this case happens to be = 0) > > and then in the code try and compare it with a signed int yyy > > the compiler ignores your signed int and does an unsigned compare. > > Up until now this hadn't been a problem as I've (quite by chance) always > looked for equality with the const. > > But today I counted yyy down to -1 wishing to stop when yyy < xxx but > the sodding compiler decided that I now had a very large positive number > and refused to break from my loop. > > Took me flipping ages to work out what was wrong :-( > > The compiler does give me a warning - that I am comparing a signed with > an unsigned but as I was sure that the unsigned int was the zero value > const I didn't give it a second thought. > > But I did get rid of it by using a c-style cast to make the unsigned int > a signed one when comparing, and that suppressed the warning, but it > still didn't work. - not until I redefined my const as signed!Post the /exact/ code, then we can see. When you do an operation (comparison or arithmetic operation), the operands are first promoted towards int, then converted to matching types. If you have an unsigned int and a signed int, the signed int will be converted to unsigned before the operation. That's the rules of C (and C++) - often it can be unexpected, so compilers can warn for such cases.> > I'm not hugely experienced with C++ [1], is it meant to work like this > or do I have a broken compiler? > > We're using GCC > > TIA > > Tim > > [1] nor are any of my co-workers, finding good ones who will work for > the pittance that embedded work now pays in my neck of the woods is tres > difficult :-) > > > >
Reply by ●March 1, 20162016-03-01
On Tue, 01 Mar 2016 22:53:34 +0100, David Brown wrote:> On 01/03/16 22:02, tim... wrote: >> Today, I discovered that if you use: >> >> const unsigned int xxx = value; (which in this case happens to be = 0) >> >> and then in the code try and compare it with a signed int yyy >> >> the compiler ignores your signed int and does an unsigned compare. >> >> Up until now this hadn't been a problem as I've (quite by chance) >> always looked for equality with the const. >> >> But today I counted yyy down to -1 wishing to stop when yyy < xxx but >> the sodding compiler decided that I now had a very large positive >> number and refused to break from my loop. >> >> Took me flipping ages to work out what was wrong :-( >> >> The compiler does give me a warning - that I am comparing a signed with >> an unsigned but as I was sure that the unsigned int was the zero value >> const I didn't give it a second thought. >> >> But I did get rid of it by using a c-style cast to make the unsigned >> int a signed one when comparing, and that suppressed the warning, but >> it still didn't work. - not until I redefined my const as signed! > > Post the /exact/ code, then we can see. > > When you do an operation (comparison or arithmetic operation), the > operands are first promoted towards int, then converted to matching > types. If you have an unsigned int and a signed int, the signed int > will be converted to unsigned before the operation. That's the rules of > C (and C++) - often it can be unexpected, so compilers can warn for such > cases.I _thought_ it was both, but I wasn't sure. I vacillate between using unsigned ints to represent whole numbers because -- hey, they're whole numbers, and using ints for everything because of the screwy things that can happen when an unsigned and a signed interact. I still haven't come up with one unified philosophy for it all, though. -- Tim Wescott Wescott Design Services http://www.wescottdesign.com
Reply by ●March 1, 20162016-03-01
Am 01.03.2016 um 22:02 schrieb tim...:> The compiler does give me a warning - that I am comparing a signed with > an unsigned but as I was sure that the unsigned int was the zero value > const I didn't give it a second thought.That reasoning was just wrong. The thing you were sure about is correct. It doesn't support the conclusion you drew, though.> But I did get rid of it by using a c-style cast to make the unsigned > int a signed one when comparing, and that suppressed the warning, but > it still didn't work. - not until I redefined my const as signed!That attempt most probably failed in some other, unrelated way. Which way it was can only be found out if you show an actual, essentially complete (as in: compilable) example case.
Reply by ●March 1, 20162016-03-01
On 3/1/2016 5:20 PM, Hans-Bernhard Br�ker wrote:> Am 01.03.2016 um 22:02 schrieb tim...: >> The compiler does give me a warning - that I am comparing a signed with >> an unsigned but as I was sure that the unsigned int was the zero value >> const I didn't give it a second thought. > > That reasoning was just wrong. The thing you were sure about is > correct. It doesn't support the conclusion you drew, though. > >> But I did get rid of it by using a c-style cast to make the unsigned >> int a signed one when comparing, and that suppressed the warning, but >> it still didn't work. - not until I redefined my const as signed! > > That attempt most probably failed in some other, unrelated way. Which > way it was can only be found out if you show an actual, essentially > complete (as in: compilable) example case.That's easy. He said he was counting down to -1 which won't work if the value is cast to an unsigned. The original problem was ignoring warnings. They aren't "errors" unless you don't understand what is happening. They do indicate you are doing something in your code that you should straighten out to make sure you know what is happening. That's way I like strong typing. You either tell the tool just what you intend it to do, or it sits down in the middle of the road until you do. -- Rick
Reply by ●March 1, 20162016-03-01
Tim Wescott wrote:> On Tue, 01 Mar 2016 22:53:34 +0100, David Brown wrote: > >> On 01/03/16 22:02, tim... wrote: >>> Today, I discovered that if you use: >>> >>> const unsigned int xxx = value; (which in this case happens to be = 0) >>> >>> and then in the code try and compare it with a signed int yyy >>> >>> the compiler ignores your signed int and does an unsigned compare. >>> >>> Up until now this hadn't been a problem as I've (quite by chance) >>> always looked for equality with the const. >>> >>> But today I counted yyy down to -1 wishing to stop when yyy < xxx but >>> the sodding compiler decided that I now had a very large positive >>> number and refused to break from my loop. >>> >>> Took me flipping ages to work out what was wrong :-( >>> >>> The compiler does give me a warning - that I am comparing a signed with >>> an unsigned but as I was sure that the unsigned int was the zero value >>> const I didn't give it a second thought. >>> >>> But I did get rid of it by using a c-style cast to make the unsigned >>> int a signed one when comparing, and that suppressed the warning, but >>> it still didn't work. - not until I redefined my const as signed! >> >> Post the /exact/ code, then we can see. >> >> When you do an operation (comparison or arithmetic operation), the >> operands are first promoted towards int, then converted to matching >> types. If you have an unsigned int and a signed int, the signed int >> will be converted to unsigned before the operation. That's the rules of >> C (and C++) - often it can be unexpected, so compilers can warn for such >> cases. > > I _thought_ it was both, but I wasn't sure. > > I vacillate between using unsigned ints to represent whole numbers > because -- hey, they're whole numbers, and using ints for everything > because of the screwy things that can happen when an unsigned and a > signed interact. I still haven't come up with one unified philosophy for > it all, though. >I pretty much only use unsigned if it's an upcounter that can go past the 0x7FF...F range, unless for some reason <stdint.h> is in play. -- Les Cargill
Reply by ●March 1, 20162016-03-01
On 01/03/16 22:12, Tim Wescott wrote:> On Tue, 01 Mar 2016 21:02:46 +0000, tim... wrote: > >> Today, I discovered that if you use: >> >> const unsigned int xxx = value; (which in this case happens to be = 0) >> >> and then in the code try and compare it with a signed int yyy >> >> the compiler ignores your signed int and does an unsigned compare. >> >> Up until now this hadn't been a problem as I've (quite by chance) always >> looked for equality with the const. >> >> But today I counted yyy down to -1 wishing to stop when yyy < xxx but >> the sodding compiler decided that I now had a very large positive number >> and refused to break from my loop. >> >> Took me flipping ages to work out what was wrong :-( >> >> The compiler does give me a warning - that I am comparing a signed with >> an unsigned but as I was sure that the unsigned int was the zero value >> const I didn't give it a second thought. >> >> But I did get rid of it by using a c-style cast to make the unsigned int >> a signed one when comparing, and that suppressed the warning, but it >> still didn't work. - not until I redefined my const as signed! >> >> I'm not hugely experienced with C++ [1], is it meant to work like this >> or do I have a broken compiler? >> >> We're using GCC >> >> TIA >> >> Tim >> >> [1] nor are any of my co-workers, finding good ones who will work for >> the pittance that embedded work now pays in my neck of the woods is tres >> difficult :-) > > 1: Yes, it's supposed to work like that. Google "automatic type > conversions". > > 2: I'm pretty sure that C is supposed to work like that, too. If it > isn't, then the behavior is implementation defined -- meaning that it > won't be predictable at all.Yes, C works the same here. The size of "int" is implementation defined, but the other conversions are fixed in the C and C++ standards. You can be sure that gcc follows the rules here - there are bugs in any compiler, but not with something that fundamental.> > 3: "by using a c-style cast to make the unsigned int a signed one when > comparing, and that suppressed the warning, but it still didn't work. - > not until I redefined my const as signed!" > > What? 'splain, please. I thought the const was the only unsigned.We need to see the code here to know exactly what is going on (including compiler version and relevant flags).> > You should use a C++ - style cast: > > if (static_cast<int>(xxx) >= yyy) > { > yadda yadda > }Well, there is more than one way to view that. C++ offers several ways to cast an unsigned "xxx" into an int: static_cast<int>(xxx) (int) xxx int(xxx) int xxx2 = xxx; // Not technically a cast, but similar effect People often say "always use C++ style static_cast", with no justification or reasoning - it's just a rule that "everybody" knows. Sometimes people /do/ try to provide justification. Two common ones are "casts are evil" (which is given as an axiom) and therefore we should use "static_cast" because it stands out, and it is easy to search for. The other reason is that C-style casts and function style casts (which are equivalent) are more powerful - they could also do const_cast or reinterpret_cast, so static_cast is more explicit. Now, casting with "complicated" types (classes, pointers, references, etc.) should always be done with great care. Using C++ style casts is a good idea there, and can give better compiler checking. But in a case like this, I would say that int(xxx) is the simplest, clearest, and easiest to include an expression, and it does exactly what it says on the tin. I think int(xxx) is a touch clearer than "(int) x" with a view to precedence and the order of the expression, but for some uses the C compatibility is useful.> > 4: Use -Wall. Then, since "all" does not, somehow, mean "all" to the > crew at GCC, use -Wextra, too. >In gcc warnings, "all" historically meant "all warnings that are not going to generate warning on a lot of common code", while "extra" (originally just "-W", not "-Wextra") meant "some more warnings including code that many people think is perfectly good". Since then, gcc has acquired many new warnings - a few have been added to -Wall, a few to -Wextra, and many are not part of either set. The gcc team have the attitude that if well-used code has compiled cleanly with -Wall or -Wall -Wextra before, then it should continue to do so with newer versions of gcc in most cases. Amongst other things, a full rebuild of the debian code base is used to test this. My makefiles contain a long list of warnings beyond -Wall -Wextra. In many cases, I insist that my own code compiles cleanly there - but a lot of other code does not. For example, gcc has a "-Wpadded" warning that triggers if there is padding added to a struct. Personally, I like to make any padding explicit, and checked with "-Wpadded", but lots of manufacturer-supplied header files will rely on implicit padding. Putting "-Wpadded" in "-Wall" would flood your build outputs with spurious errors.
Reply by ●March 2, 20162016-03-02
On 01/03/16 22:12, Tim Wescott wrote:> On Tue, 01 Mar 2016 21:02:46 +0000, tim... wrote: > >> Today, I discovered that if you use: >> >> const unsigned int xxx = value; (which in this case happens to be = 0) >> >> and then in the code try and compare it with a signed int yyy >> >> the compiler ignores your signed int and does an unsigned compare. >> >> Up until now this hadn't been a problem as I've (quite by chance) always >> looked for equality with the const. >> >> But today I counted yyy down to -1 wishing to stop when yyy < xxx but >> the sodding compiler decided that I now had a very large positive number >> and refused to break from my loop. >> >> Took me flipping ages to work out what was wrong :-( >> >> The compiler does give me a warning - that I am comparing a signed with >> an unsigned but as I was sure that the unsigned int was the zero value >> const I didn't give it a second thought. >> >> But I did get rid of it by using a c-style cast to make the unsigned int >> a signed one when comparing, and that suppressed the warning, but it >> still didn't work. - not until I redefined my const as signed! >> >> I'm not hugely experienced with C++ [1], is it meant to work like this >> or do I have a broken compiler? >> >> We're using GCC >> >> TIA >> >> Tim >> >> [1] nor are any of my co-workers, finding good ones who will work for >> the pittance that embedded work now pays in my neck of the woods is tres >> difficult :-) > > 1: Yes, it's supposed to work like that. Google "automatic type > conversions". > > 2: I'm pretty sure that C is supposed to work like that, too. If it > isn't, then the behavior is implementation defined -- meaning that it > won't be predictable at all.Yes, C works the same here. The size of "int" is implementation defined, but the other conversions are fixed in the C and C++ standards. You can be sure that gcc follows the rules here - there are bugs in any compiler, but not with something that fundamental.> > 3: "by using a c-style cast to make the unsigned int a signed one when > comparing, and that suppressed the warning, but it still didn't work. - > not until I redefined my const as signed!" > > What? 'splain, please. I thought the const was the only unsigned.We need to see the code here to know exactly what is going on (including compiler version and relevant flags).> > You should use a C++ - style cast: > > if (static_cast<int>(xxx) >= yyy) > { > yadda yadda > }Well, there is more than one way to view that. C++ offers several ways to cast an unsigned "xxx" into an int: static_cast<int>(xxx) (int) xxx int(xxx) int xxx2 = xxx; // Not technically a cast, but similar effect People often say "always use C++ style static_cast", with no justification or reasoning - it's just a rule that "everybody" knows. Sometimes people /do/ try to provide justification. Two common ones are "casts are evil" (which is given as an axiom) and therefore we should use "static_cast" because it stands out, and it is easy to search for. The other reason is that C-style casts and function style casts (which are equivalent) are more powerful - they could also do const_cast or reinterpret_cast, so static_cast is more explicit. Now, casting with "complicated" types (classes, pointers, references, etc.) should always be done with great care. Using C++ style casts is a good idea there, and can give better compiler checking. But in a case like this, I would say that int(xxx) is the simplest, clearest, and easiest to include an expression, and it does exactly what it says on the tin. I think int(xxx) is a touch clearer than "(int) x" with a view to precedence and the order of the expression, but for some uses the C compatibility is useful.> > 4: Use -Wall. Then, since "all" does not, somehow, mean "all" to the > crew at GCC, use -Wextra, too. >In gcc warnings, "all" historically meant "all warnings that are not going to generate warning on a lot of common code", while "extra" (originally just "-W", not "-Wextra") meant "some more warnings including code that many people think is perfectly good". Since then, gcc has acquired many new warnings - a few have been added to -Wall, a few to -Wextra, and many are not part of either set. The gcc team have the attitude that if well-used code has compiled cleanly with -Wall or -Wall -Wextra before, then it should continue to do so with newer versions of gcc in most cases. Amongst other things, a full rebuild of the debian code base is used to test this. My makefiles contain a long list of warnings beyond -Wall -Wextra. In many cases, I insist that my own code compiles cleanly there - but a lot of other code does not. For example, gcc has a "-Wpadded" warning that triggers if there is padding added to a struct. Personally, I like to make any padding explicit, and checked with "-Wpadded", but lots of manufacturer-supplied header files will rely on implicit padding. Putting "-Wpadded" in "-Wall" would flood your build outputs with spurious errors.
Reply by ●March 2, 20162016-03-02
On 02/03/16 03:57, Les Cargill wrote:> Tim Wescott wrote: >> On Tue, 01 Mar 2016 22:53:34 +0100, David Brown wrote: >> >>> On 01/03/16 22:02, tim... wrote: >>>> Today, I discovered that if you use: >>>> >>>> const unsigned int xxx = value; (which in this case happens to be = 0) >>>> >>>> and then in the code try and compare it with a signed int yyy >>>> >>>> the compiler ignores your signed int and does an unsigned compare. >>>> >>>> Up until now this hadn't been a problem as I've (quite by chance) >>>> always looked for equality with the const. >>>> >>>> But today I counted yyy down to -1 wishing to stop when yyy < xxx but >>>> the sodding compiler decided that I now had a very large positive >>>> number and refused to break from my loop. >>>> >>>> Took me flipping ages to work out what was wrong :-( >>>> >>>> The compiler does give me a warning - that I am comparing a signed with >>>> an unsigned but as I was sure that the unsigned int was the zero value >>>> const I didn't give it a second thought. >>>> >>>> But I did get rid of it by using a c-style cast to make the unsigned >>>> int a signed one when comparing, and that suppressed the warning, but >>>> it still didn't work. - not until I redefined my const as signed! >>> >>> Post the /exact/ code, then we can see. >>> >>> When you do an operation (comparison or arithmetic operation), the >>> operands are first promoted towards int, then converted to matching >>> types. If you have an unsigned int and a signed int, the signed int >>> will be converted to unsigned before the operation. That's the rules of >>> C (and C++) - often it can be unexpected, so compilers can warn for such >>> cases. >> >> I _thought_ it was both, but I wasn't sure. >> >> I vacillate between using unsigned ints to represent whole numbers >> because -- hey, they're whole numbers, and using ints for everything >> because of the screwy things that can happen when an unsigned and a >> signed interact. I still haven't come up with one unified philosophy for >> it all, though. >>Unsigned and signed types have slightly different properties, and they have their advantages and disadvantages. In particular, overflow behaviour of unsigned types is well defined (see below for exceptions) using modular arithmetic, while for signed types it is explicitly undefined. If you want defined overflow behaviour, use unsigned types. But sometimes the undefined nature of signed overflow leads to more efficient code (since the compiler can assume it won't happen, and optimise accordingly). There are a few cases where unsigned arithmetic can lead to undefined behaviour. In particular, if you have an unsigned type that is of a smaller size than an int, then integer promotion rules say that the values are promoted to signed int, which can have undefined overflow behaviour. For example, assume we have a 32-bit int, and consider the function: uint16_t mult16(uint16_t a, uint16_b) { return a * b; } In practice, the compiler will use whatever is the most convenient unsigned multiply instruction, and truncate the result modulo 16-bit. However, /logically/ (as used in inlining, constant propagation, etc.), the function is this: uint16_t mult16(uint16_t a, uint16_b) { int32_t a1 = (int32_t) a; int32_t b1 = (int32_t) b; int32_t res1 = a1 * b1; uint16_t res = ((uint32_t) res1) & 0xffff; return res; } If this is called with a and b equal to 0xffff, the 32-bit signed multiplication will overflow - it is undefined behaviour. You can also get undefined behaviour with shifts of unsigned types, similar to those for signed types. As for one unified philosophy, I don't think it is possible to get one. "int" and "unsigned int" (and the <stdint.h> types) have overlapping ranges - as long as there are legal int values that cannot be represented cleanly in an unsigned int, and vice versa, there will always be issues. The C conversion and promotion rules are designed so that most things work most of the time without any extra explicit conversions - but sometimes they are simply wrong. Be cautious, be generous with your casts when in doubt, and be generous with your warning flags (like -Wsign-conversion and -Wsign-compare).> > > I pretty much only use unsigned if it's an upcounter that can go past > the 0x7FF...F range, unless for some reason <stdint.h> is in play. >This is comp.arch.embedded - <stdint.h> should /always/ be in play!







