Comparing uint64_t variables

Hello,

I want to test some bits (bits 40 to 47) of a uint64_t variable for a certain value (69). So I shift the variable 40 bits to the right, mask it with 255 and check if the value is 69. Everything works as expected, the code between {} is called if bits 40 to 47 of BitFolge contain the 8-bit number 69.

uint64_t BitFolge = ...;
if (((BitFolge>>40) & 255ULL) == 69ULL)
{
...
}

But if I instead shift the mask and the value I want to compare with by 40 bits to the left, it does not work, the code between {} is never called.

uint64_t BitFolge = ...;
if ((BitFolge & (255ULL<<40)) == (69ULL<<40))
{
...
}

Shouldn't it work also this way? I use the arduino IDE 2.2.1 with an arduino uno.

Thanks for replies.

It seems that it should work, but 64 bit integer operations have never been fully supported by the AVR Arduinos (you can't directly print a 64 bit value, for example), and perhaps not on other Arduinos as well.

1 Like

For inspection of results, this code will print the upper and lower 32 bit sections of a 64 bit integer:

void setup() {
Serial.begin(115200);
while (!Serial);

uint64_t x=(69ULL<<40);
Serial.println((uint32_t)(x>>32));
Serial.println((uint32_t)(x&0xFFFFFFFF));

x = (69ULL<<8);
Serial.println((uint32_t)(x>>32));
Serial.println((uint32_t)(x&0xFFFFFFFF));
}

void loop() {
}
1 Like

please show a full compileable sketch.

because this will work as planned - it will print yes

void setup() {
  Serial.begin(115200);
  Serial.println("64bit shifting");
  uint64_t BitFolge = 69ULL << 40;
  //uint64_t BitFolge = 0b010001010000000000000000000000000000000000000000;
  if ((BitFolge & (255ULL << 40)) == (69ULL << 40))
  {
    Serial.println("yes");
  }


}

void loop() {
  // put your main code here, to run repeatedly:

}

tested on wokwi and on real hardware (Arduino Nano Clone)

1 Like

Hi,

thank you all for your replies. I made a mistake and forgot that it is a volatile variable because it is used in an interrupt function. This code:

void setup()
{
  Serial.begin(250000);
  uint64_t BitFolge = (69ULL << 40);
  if ((BitFolge & (255ULL << 40)) == (69ULL << 40))
  {
    Serial.println("yes left shift");
  }
  if (((BitFolge>>40) & 255ULL) == 69ULL)
  {
    Serial.println("yes right shift");
  }

  volatile uint64_t BitFolge_volatile = (69ULL << 40);
  if ((BitFolge_volatile & (255ULL << 40)) == (69ULL << 40))
  {
    Serial.println("yes left shift volatile");
  }
  if (((BitFolge_volatile>>40) & 255ULL) == 69ULL)
  {
    Serial.println("yes right shift volatile");
  }
}

void loop()
{

}

has this output:

yes left shift
yes right shift
yes right shift volatile

If I use an uint32_t variable (shift only by 11 bits to fit it into 32 bits), everything works fine:

void setup()
{
  Serial.begin(250000);
  uint32_t BitFolge = (69ULL << 11);
  if ((BitFolge & (255ULL << 11)) == (69ULL << 11))
  {
    Serial.println("yes left shift");
  }
  if (((BitFolge>>11) & 255ULL) == 69ULL)
  {
    Serial.println("yes right shift");
  }

  volatile uint32_t BitFolge_volatile = (69ULL << 11);
  if ((BitFolge_volatile & (255ULL << 11)) == (69ULL << 11))
  {
    Serial.println("yes left shift volatile");
  }
  if (((BitFolge_volatile>>11) & 255ULL) == 69ULL)
  {
    Serial.println("yes right shift volatile");
  }
}

gives:

yes left shift
yes right shift
yes left shift volatile
yes right shift volatile

How can the volatile property affect the comparison? Both volatile and non volatile variables contain the same data:

void setup()
{
  Serial.begin(250000);
  uint64_t BitFolge = (69ULL << 40);
  Serial.println((uint32_t)(BitFolge>>32));
  Serial.println((uint32_t)(BitFolge&0xFFFFFFFF));
 
  volatile uint64_t BitFolge_volatile = (69ULL << 40);
  Serial.println((uint32_t)(BitFolge_volatile>>32));
  Serial.println((uint32_t)(BitFolge_volatile&0xFFFFFFFF));
}

gives:

17664
0
17664
0

Does no one have any idea, why volatile uint64_t variables are treated differently from non volatile ones?

Works as expected on an ESP32 Dev Board:

yes left shift
yes right shift
yes left shift volatile
yes right shift volatile
1 Like

Volatile note:

If BitFolge is volatile you must make a copy of it before comparing.
Otherwise it is possible that the value changes during comparison or during the shift or both.

nointerrupts();
uint64_t BFcopy = BitFolge
interrupts();

//  compare ...
1 Like

Not in the code supplied in Post #6. In that code the volatile variable is local. And there is no other function (i.e. ISR) accessing it.

1 Like

You are 100% right, however there will be a reason to make a variable volatile other than to challenge the compiler. (OK that is an assumption too).

1 Like

yes left shift
yes right shift
yes left shift volatile
yes right shift volatile

Interesting, on my 8-bit arduino uno uint32_t works correctly, but not uint64_t.
On the 32-bit esp32 uint64_t works. Maybe the uint64_t data type is really not yet completely implemented on 8-bit arduino systems, like jremington said.

I had made the variable volatile because in the final program it should be written in a pin change interrupt function that is triggered by a 433MHz receiver output pin. But in the code block posted, I had removed all this to show the problem.

EDIT: No, wait. This is code that printed correct results, isn't it?

It looks the the compiler is using the wrong shift function:

void loop() {
  uint64_t BitFolge = (69ULL << 40);
  Serial.println((uint32_t)(BitFolge >> 32));
;;; for the non volatile case, the compiler simply optimizes away the math,
;;; and uses the resulting constant as argument to println
 6a0:   15 e4           ldi     r17, 0x45       ; 69
  :
 6aa:   60 e0           ldi     r22, 0x00       ; 0
 6ac:   75 e4           ldi     r23, 0x45       ; 69
 6ae:   80 e0           ldi     r24, 0x00       ; 0
 6b0:   90 e0           ldi     r25, 0x00       ; 0
 6b2:   0e 94 d0 01     call    0x3a0   ; 0x3a0 <Print::println(unsigned long, int)
  Serial.println((uint32_t)(BitFolge & 0xFFFFFFFF));
 6b6:   60 e0           ldi     r22, 0x00       ; 0
 6b8:   70 e0           ldi     r23, 0x00       ; 0
 6ba:   cb 01           movw    r24, r22
 6bc:   0e 94 d0 01     call    0x3a0   ; 0x3a0 <Print::println(unsigned long, int) [clone .constprop.3]>

;;; For the volatile case, optimization is defeated.
  volatile uint64_t BitFolge_volatile = (69ULL << 40);
;;; initialize the constant.
 6c0:   19 82           std     Y+1, r1 ; 0x01
 6c2:   1a 82           std     Y+2, r1 ; 0x02
 6c4:   1b 82           std     Y+3, r1 ; 0x03
 6c6:   1c 82           std     Y+4, r1 ; 0x04
 6c8:   1d 82           std     Y+5, r1 ; 0x05
 6ca:   1e 83           std     Y+6, r17        ;;; still contains 0x45
 6cc:   1f 82           std     Y+7, r1 ; 0x07
 6ce:   18 86           std     Y+8, r1 ; 0x08
  Serial.println((uint32_t)(BitFolge_volatile >> 32));
;;; load the constant.
 6d0:   29 81           ldd     r18, Y+1        ; 0x01
 6d2:   3a 81           ldd     r19, Y+2        ; 0x02
 6d4:   4b 81           ldd     r20, Y+3        ; 0x03
 6d6:   5c 81           ldd     r21, Y+4        ; 0x04
 6d8:   6d 81           ldd     r22, Y+5        ; 0x05
 6da:   7e 81           ldd     r23, Y+6        ; 0x06
 6dc:   8f 81           ldd     r24, Y+7        ; 0x07
 6de:   98 85           ldd     r25, Y+8        ; 0x08
;;; shift by 32.  EXCEPT __lshrdi3 is a 32bit shift.  Should be __lshrti3
 6e0:   00 e2           ldi     r16, 0x20       ; 32
 6e2:   0e 94 e6 03     call    0x7cc   ; 0x7cc <__lshrdi3>
;;; extract 32bits for printing.
 6e6:   b9 01           movw    r22, r18
 6e8:   ca 01           movw    r24, r20
 6ea:   0e 94 d0 01     call    0x3a0   ; 0x3a0 <Print::println(unsigned long, int)
;;; reload the number and extract the low bits for printing.
  Serial.println((uint32_t)(BitFolge_volatile & 0xFFFFFFFF));
 6ee:   59 81           ldd     r21, Y+1        ; 0x01
 6f0:   4a 81           ldd     r20, Y+2        ; 0x02
 6f2:   3b 81           ldd     r19, Y+3        ; 0x03
 6f4:   2c 81           ldd     r18, Y+4        ; 0x04
 6f6:   8d 81           ldd     r24, Y+5        ; 0x05
 6f8:   8e 81           ldd     r24, Y+6        ; 0x06
 6fa:   8f 81           ldd     r24, Y+7        ; 0x07
 6fc:   88 85           ldd     r24, Y+8        ; 0x08
 6fe:   65 2f           mov     r22, r21
 700:   74 2f           mov     r23, r20
 702:   83 2f           mov     r24, r19
 704:   92 2f           mov     r25, r18
 706:   0e 94 d0 01     call    0x3a0   ; 0x3a0 <Print::println(unsigned long, int) 
1 Like

googling....

Integer library routines - GNU Compiler Collection (GCC) Internals :

— Runtime Function: int __lshrsi3 (int a, int b)
— Runtime Function: long __lshrdi3 (long a, int b)
— Runtime Function: long long __lshrti3 (long long a, int b)

These functions return the result of logically shifting a right by b bits.

1 Like

Yeah, I did that, which led to my conclusion. But I don't think that's for AVR, or something.
Because:

The failing code in #6 compiles to:

if ((BitFolge_volatile & (255ULL << 40)) == (69ULL << 40))
57c:   89 81           ldd     r24, Y+1        ; 0x01
57e:   8a 81           ldd     r24, Y+2        ; 0x02
580:   8b 81           ldd     r24, Y+3        ; 0x03
582:   8c 81           ldd     r24, Y+4        ; 0x04
584:   8d 81           ldd     r24, Y+5        ; 0x05
586:   8e 81           ldd     r24, Y+6        ; 0x06
588:   8f 81           ldd     r24, Y+7        ; 0x07
58a:   88 85           ldd     r24, Y+8        ; 0x08
 {
   Serial.println("yes left shift volatile");
 }
 if (((BitFolge_volatile>>40) & 255ULL) == 69ULL)
58c:   29 81           ldd     r18, Y+1        ; 0x01
58e:   3a 81           ldd     r19, Y+2        ; 0x02
590:   4b 81           ldd     r20, Y+3        ; 0x03

ie, BitFolge_volatile is loaded, because it is volatile, but for some reason the compiler seems to think that the comparison can NEVER be true, and has therefore omitted the contents of the if clause. (and moves on to the next if)
Weird.

1 Like

Here's a simpler version that illustrates the bug.

#include <avr/io.h>
#include <stdint.h>

int main() {
  volatile uint64_t y64vol = (69ULL << 40);
  if ((y64vol & (255ULL<<40)) == (69ULL<<40)) {
    PORTB = 0xFF;
  }
}

Compile with avr-gcc -mmcu=atmega328 -g t_int64.c -Os -nostartfiles
and get:

int main() {
;;; set up stack frame for y64vol
   0:	cf 93       	push	r28
   2:	df 93       	push	r29
   4:	cd b7       	in	r28, 0x3d	; 61
   6:	de b7       	in	r29, 0x3e	; 62
   8:	28 97       	sbiw	r28, 0x08	; 8
   a:	0f b6       	in	r0, 0x3f	; 63
   c:	f8 94       	cli
   e:	de bf       	out	0x3e, r29	; 62
  10:	0f be       	out	0x3f, r0	; 63
  12:	cd bf       	out	0x3d, r28	; 61
  volatile uint64_t y64vol = (69ULL << 40);
;;; initialize y64vol
  14:	19 82       	std	Y+1, r1	; 0x01
  16:	1a 82       	std	Y+2, r1	; 0x02
  18:	1b 82       	std	Y+3, r1	; 0x03
  1a:	1c 82       	std	Y+4, r1	; 0x04
  1c:	1d 82       	std	Y+5, r1	; 0x05
  1e:	85 e4       	ldi	r24, 0x45	; 69
  20:	8e 83       	std	Y+6, r24	; 0x06
  22:	1f 82       	std	Y+7, r1	; 0x07
  24:	18 86       	std	Y+8, r1	; 0x08
  if ((y64vol & (255ULL<<40)) == (69ULL<<40)) {
;;; load y64val, since it was volatile...
  26:	89 81       	ldd	r24, Y+1	; 0x01
  28:	8a 81       	ldd	r24, Y+2	; 0x02
  2a:	8b 81       	ldd	r24, Y+3	; 0x03
  2c:	8c 81       	ldd	r24, Y+4	; 0x04
  2e:	8d 81       	ldd	r24, Y+5	; 0x05
  30:	7e 81       	ldd	r23, Y+6	; 0x06
  32:	8f 81       	ldd	r24, Y+7	; 0x07
  34:	88 85       	ldd	r24, Y+8	; 0x08
;;; no actual test, no reference to PORTB anywhere in the code.
    PORTB = 0xFF;
  }
;;; return value for main (0)
  36:	90 e0       	ldi	r25, 0x00	; 0
  38:	80 e0       	ldi	r24, 0x00	; 0
;;; clear up stack frame and actually return.
  3a:	28 96       	adiw	r28, 0x08	; 8
  3c:	0f b6       	in	r0, 0x3f	; 63
  3e:	f8 94       	cli
  40:	de bf       	out	0x3e, r29	; 62
  42:	0f be       	out	0x3f, r0	; 63
  44:	cd bf       	out	0x3d, r28	; 61
  46:	df 91       	pop	r29
  48:	cf 91       	pop	r28
  4a:	08 95       	ret
}
3 Likes

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.