Due unsigned long long int issue

Hi all

I've looked around the web and on the forum about an issue with long long ints on an Arduino Due but all the articles seem to be for the UNO. In the following code, if I look at the ASM output seems to be using 32 bit ints not matter whether I use u_int64 or long long int. I've added 'ULL' to end of the assignment numbers too

  #include <stdlib.h>
  #include <stdio.h>
  #include <Arduino.h>


  void setup() {
  // put your setup code here, to run once:

  unsigned long long int bit64 = 0, i64;

  unsigned long long int *a2;
  int i;


  bit64 = 5000000000ULL;

  for (i = 0; i < 10; i++) {
    i64 = i;
    bit64 = bit64 + log((unsigned long long int) i);


    bit64++;


  }

  Serial.println();

  Serial.println(int(bit64));
  }

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

  }

Any ideas?

Bipman

You need uint64_t, int64_t or double types. Note that Serial.print will not print these variables as is, you will need a workaround to print them.

See this snippet:

void setup() {
 
Serial.begin(250000);

uint64_t var1_64 = 0;
int64_t var2_64 = 0;
double var3_64;  // double float
Serial.print(" Size of var1_64 = "); Serial.println(sizeof(var1_64));
Serial.print(" Size of var2_64 = "); Serial.println(sizeof(var2_64));
Serial.print(" Size of var3_64 = "); Serial.println(sizeof(var3_64));
}
void loop() {

}

In the following code, if I look at the ASM output seems to be using 32 bit ints not matter whether I use u_int64 or long long int.

It looks OK to me. Can you be more specific?

    bit64 = bit64 + log((unsigned long long int) i);

That's a bit useless, since log() takes a double as the argument.

Here's what I get after a bit of editing, and adding comments...

00080148 <setup>:
  void setup() {
  unsigned long long int *a2;
  int i;
;;; Prologue
   80148:       e92d 43f8       stmdb   sp!, {r3, r4, r5, r6, r7, r8, r9, lr}


  bit64 = 5000000000ULL;
;;; load a 64bit constant.   Note the use of ldrd - a 64bit move.
   8014c:       a717            add     r7, pc, #92     ; (adr r7, 801ac <setup+0x64>)
   8014e:       e9d7 6700       ldrd    r6, r7, [r7]


  for (i = 0; i < 10; i++) {
   80152:       2400            movs    r4, #0
   80154:       2500            movs    r5, #0
    i64 = i;
    bit64 = bit64 + log((unsigned long long int) i);
   80156:       4620            mov     r0, r4
   80158:       4629            mov     r1, r5
;;; __eaabi_ul2d is 64bit int to double float.
   8015a:       f002 f987       bl      8246c <__aeabi_ul2d>
   8015e:       f001 fd91       bl      81c84 <log>
   80162:       4680            mov     r8, r0
   80164:       4689            mov     r9, r1
   80166:       4630            mov     r0, r6
   80168:       4639            mov     r1, r7
   8016a:       f002 f97f       bl      8246c <__aeabi_ul2d>
   8016e:       4642            mov     r2, r8
   80170:       464b            mov     r3, r9
;;; Still dealing with doubles at this point, for the add/
   80172:       f001 ffff       bl      82174 <__adddf3>
;;; convert double back to 64bit int
   80176:       f002 fc69       bl      82a4c <__aeabi_d2ulz>

    bit64++;
;;; 64bit add - note use of two registers, and adc instruction
   8017a:       1c46            adds    r6, r0, #1
   8017c:       f141 0700       adc.w   r7, r1, #0
;;; Um.  Looks like the compiler has "optimized" the loop counter to 64bits too?
   80180:       3401            adds    r4, #1
   80182:       f145 0500       adc.w   r5, r5, #0

   80186:       2d00            cmp     r5, #0
   80188:       bf08            it      eq
   8018a:       2c0a            cmpeq   r4, #10
   8018c:       d1e3            bne.n   80156 <setup+0xe>
  }

You need uint64_t, int64_t or double types.

BTW: "long long" should be fine. int64_t was added later, and is preferred, since all the C spec actually guarantees is that a "long long" is at least as big as a "long", but they're the same. If you have more patience than I do, you can probably track down that *somewhere" there is the equivalent of "typedef unsigned long long uint64_t;" (it seems to be complicated by those 64bit ARM compiler models where a plain "long" is also 64bits.)

Thanks all, I was misreading the ASM output :blush:

Bipman

[...] where a plain "long" is also 64bits.

I hope that I am not confused!

1. In Arduino UNO: long (or long int) is 32-bit.

2. In Arduino DUE: long (or long int) is 32-bit.
3. In Arduino DUE: long long (or long long int) is 64-bit (plain)
4. In Arduino DUE: double: 64-bit (floating)

I hope that I am not confused!

  1. In Arduino UNO: long (or long int) is 32-bit.

  2. In Arduino DUE: long (or long int) is 32-bit.

  3. In Arduino DUE: long long (or long long int) is 64-bit (plain)

  4. In Arduino DUE: double: 64-bit (floating)

That all looks right. When I said "64bit ARMs", I mean the chips in your phone and such, that have hardware support for 64bit integers and more than 32bits of address space. (also applies to x86.)
When writing code for THOSE, you have a choice of several different "models" that define the lengths of "int", "long", and pointers. You CAN just make everything 64bits, but that tends to be regarded as pretty wasteful of memory, and there's all that "legacy" code that assumes the size of various types instead of using intxx_t that will break, and all that casually poorly-written code that assumes that a pointer and a long are the same length, and so on...
http://www.unix.org/version2/whatsnew/lp64_wp.html