How big is ~uint8_t?

Go To Last Post
21 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
SERIAL_RESPONSE tx_bosch (int fd, uint8_t tx_byte, RESPONSE_TYPE response)
{
	uint8_t rx_byte;

        < stuff that sends tx_byte and receives rx_byte >

	retval = ((uint8_t)tx_byte == (uint8_t)~rx_byte) ? RESPONSE_OK : RESPONSE_BAD;

Bit of an odd one here: I'm failing a comms protocol unless I explicitly cast uint8_t sized variables to uint8_t, and I don't understand why... GCC under a 64 bit linux, so a generic C query.

 

I've chopped a load of unnecessary code here, but I'm sending tx_byte and expecting the bit inverse of it back. If I don't do the cast (uint8_t)~rx_byte it seems to comparing with 0xffffffxx, where the xx is the correct inverse. But as you can see, both tx_byte and rx_byte are uint8_t - so why is the ~ operator extending to 32 bits? (I don't think the cast is required on tx_byte, I just left it there for symmetry :)

 

Actually, thinking about it, it might be extending to 64 bits; I'm printing it with %02x which I think is limited to 32 bits. Need to check further... but either way, the question remains: why is the bit inverse extending the size of the result? It's an unsigned variable, so it's not really a sign extension, but it looks like that's what's happening along with an implicit cast. /me is not happy.

 

Thanks,

 

Neil

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

INT02-C. Understand integer conversion rules

 

Edit:

 

Can't link directly to it, but there is a table about a third of the way down the page that covers your exact scenario.

build-avr-gcc: avr-gcc build script

toolchain-avr-gcc: CMake toolchain for cross compiling for the AVR family of microcontrollers

avr-libcpp: C++ standard library partial implementation (C++17 only) for use with avr-gcc/avr-libc

picolibrary: C++ microcontroller driver/utility library targeted for use with resource constrained microcontrollers

picolibrary-microchip-megaavr: picolibrary HIL for megaAVR microcontrollers

picolibrary-microchip-megaavr0: picolibrary HIL for megaAVR 0-series microcontrollers

Last Edited: Tue. Feb 23, 2021 - 10:26 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yup, that's the baby. Thanks. It's what you might call a bit unintuitive that an eight bit variable can hold a 32 bit value...

 

I hate C's implicit promotions.

 

Neil

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Never seen the "~" operator. What is it supposed to do?

 

Jim

 

Until Black Lives Matter, we do not have "All Lives Matter"!

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

"Dare to be naïve." - Buckminster Fuller

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ka7ehk wrote:
Never seen the "~" operator

Really? 

 

Time to review https://www.avrfreaks.net/forum/tut-c-bit-manipulation-aka-programming-101 then!

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Arrrg! Of Course. Never seen it combined with a cast that way, I guess. Looked like something totally alien.

 

Duhh!

 

Jim

 

Until Black Lives Matter, we do not have "All Lives Matter"!

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

A good approach is sometimes to take the verbose route when dealing with potential int conversions, but you have to already know a potential problem is lurking so you still miss the not so obvious ones.

 

It may end up being easier to read if you can avoid the casting. Compiler doesn't care which version you choose (as long as correct), so go with the whatever is more readable to you.

 

SERIAL_RESPONSE tx_bosch (int fd, uint8_t tx_byte, RESPONSE_TYPE response)
{
    uint8_t rx_byte;

        < stuff that sends tx_byte and receives rx_byte >

    rx_byte = ~rx_byte; //this should be tx_byte inverted, invert to match

    retval = (tx_byte == rx_byte) ? RESPONSE_OK : RESPONSE_BAD;

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It should mean approximately.

rx_approximat = ~rx_exact;

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

All in all, it's really a bit subtle. Once one is aware of the situation the result makes complete sense, and it's an easy fix, but I don't recall ever seeing any discussion of the subject.

 

One might be aware that implicit conversions are going on all the time, but this is really well hidden - there is (on GCC) no warning that you are comparing an eight bit value with a 32 bit value (and neither in what is entirely analogous, comparing a uint8_t with -1 instead of 0xff).

 

Perhaps I still think too much in assembly, after forty years... perhaps I should look at Rust instead?

 

Neil

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

barnacle wrote:
there is (on GCC) no warning that you are comparing an eight bit value with a 32 bit value (

Very interesting:

I tested this on Microchip xc32-gcc

#define RESPONSE_OK 0
#define RESPONSE_BAD 1
int BarnacleTest (uint8_t rx_byte, uint8_t tx_byte)
{
	int retval = ((uint8_t)tx_byte == (uint8_t)~rx_byte) ? RESPONSE_OK : RESPONSE_BAD;
	return retval;
}

and I get: (please ignore the filename)

 

../Kanelib/Drivers/SerialFlash/SST25.c: In function 'BarnacleTest':

../Kanelib/Drivers/SerialFlash/SST25.c:223:33: warning: comparison of promoted ~unsigned with unsigned [-Wsign-compare]
  int retval = ((uint8_t)tx_byte == (uint8_t)~rx_byte) ? RESPONSE_OK : RESPONSE_BAD;
                                 ^

 I'm struggling a but now to explain why the warning exists.

 

Last Edited: Wed. Feb 24, 2021 - 08:33 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

In avr-gcc 7.3.0, gcc needs -Wextra to show the promotion warning (same warning as post #11), and g++ just needs -Wall. Not a bad idea to also turn on -Werror as the warnings scroll by unseen many times.

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

N.Winterbottom wrote:
I tested this on Microchip xc32-gcc
With avr-gcc (MCS7) I get:

		.././main.c: In function 'BarnacleTest':
D:\test\mcs7_test\main.c(7,36): warning: comparison of promoted ~unsigned with unsigned [-Wsign-compare]
		     int retval = ((uint8_t)tx_byte == (uint8_t)~rx_byte) ? RESPONSE_OK : RESPONSE_BAD;
		                                    ^

Note However I always have -Wextra and -pedantic enabled - without those it's true there is no warning.

 

It is -Wextra that causes it to appear which rather argues for why that is not the default in MCS7. (I realise that the use of -pedantic is more contentious and probably should not be the default)

Last Edited: Wed. Feb 24, 2021 - 09:28 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

And if you are used to ASM I don't understand why you don't just add the two numbers ;)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

~uint8_t is always an int.

int, by definition, is supposed to be the smallest or fastest integer for convenient computation that is at least 16 bits.

Smaller integers get promoted to int before computation.

If the same size as int, unsigned short and unsigned char get promoted to unsigned int.

uint8_t is never the same size as int.

Moderation in all things. -- ancient proverb

Last Edited: Wed. Feb 24, 2021 - 02:33 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

As you can see the default Eclipse IDE settings include -Wall but neither including the cast or not raises a warning (other warnings, the code is unfinished, but nothing relating to that).

 

Skeeve summarises it in his first line - but one might expect in that case that both halves of an equality comparison should be promoted to the same size?[1] It seems to me that comparing two things of different size should always be unequal, but should also always be an error, not a warning, and certainly not invisible.

 

One for the language lawyers, I guess. I've ranted previously about hidden promotions...

 

Neil

 

[1] Ah, of course: the compiler is promoting tx_byte, which is uint_8, to a positive value int - positive by default. It does the same for rx_byte, which in this case is negative (and will always be the opposite sign to tx_byte), before it does the bit complement... correct but intuitively wrong.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

barnacle wrote:

As you can see the default Eclipse IDE settings include -Wall but neither including the cast or not raises a warning (other warnings, the code is unfinished, but nothing relating to that).

Yeah but, it is -Wextra not -Wall that is required (well you do need that too). So it should be:

 

 

PS I'd also query -O0 versus either -O1 (better) or -Og (best) for "Debug" build.

 

Last Edited: Wed. Feb 24, 2021 - 05:39 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

This is definitely a useful warning, however on the version of avr-gcc i have here

avr-gcc (GCC) 4.9.2

I still get the warning with the following code. The cast back to uint8_t after the ~ should silence the warning (ie. it is cast back to uint8_t before then being promoted to int for the comparison) but it doesn't silence the warning

    uint8_t x = PINB;
    uint8_t y = PINC;
    if (x == (uint8_t)~y)
        PORTD = 0xff;

 

Using unsigned char instead of uint8_t and the warning is then silenced by the cast, very strange!

    unsigned char x = PINB;
    unsigned char y = PINC;
    if (x == (unsigned char)~y)
        PORTD = 0xff;

 

To avoid warning using uint8_t, you have to use intermediate variable rather than cast

    uint8_t x = PINB;
    uint8_t y = PINC;
    y = ~y;
    if (x == y)
        PORTD = 0xff;

 

This sure looks like a bug in gcc, albeit very minor (EDIT because the generated code is correct, just the warning not getting silenced). Wonder if is fixed in later version?

Looks very much like https://gcc.gnu.org/bugzilla/sho...

Last Edited: Wed. Feb 24, 2021 - 11:07 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

MrKendo wrote:
I still get the warning with the following code.
No warning from a linter.

#include <stdint.h>

uint8_t PINB = 4;
uint8_t PINC = 6;
uint8_t PORTD;

int main() {
    uint8_t x = PINB;
    uint8_t y = PINC;
    if (x == (uint8_t)~y) {
        PORTD = 0xff;
    }
}

MrKendo wrote:
Wonder if is fixed in later version?
Wonder what a verified compiler generates.

 


PC-lint Plus Online Demo - Gimpel Software - The Leader in Static Analysis for C and C++ with PC-lint Plus (based on LLVM's Clang)

 

CompCert - Main page

 

"Dare to be naïve." - Buckminster Fuller

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yah, found that the warning occurs with -Wextra (thanks Cliff) and is not silenced by the cast. That does feel wrong.

 

It's the same bug as MrKendo found, I think, which looks as though the compiler isn't keeping track of the size of typedefed variables properly.

 

I want the warning on, but I don't want to see it... so I'll use the temporary variable approach instead. So much for brevity...

 

Neil

 

p.s. so there's a bug in the warnings system: -Wall does not include all warnings...cheeky

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm glad someone else thinks this is a gcc bug; because I cannot explain why it occurs.

 

Here's another way to silence this buggy warning:

	int retval = (tx_byte == (~rx_byte & 0xff)) ? RESPONSE_OK : RESPONSE_BAD;