Problems when using long long


#1

I am doing some calculations that require me to use long long but the output is not correct.

This works:

int32_t total = 10; //can be anything between 10 and -10. Stored as int32_t because of previous calculations
int32_t compensation = total * 10000;
Serial.printf("compensation: %d. total: %d. PCSCALEVAL: %d\r\n", compensation, total, 10000);

output: “compensation: 100000. total: 10. PCSCALEVAL: 10000”

but this does not:

#define PCSCALEVAL 100000000
int32_t total = 10; //can be anything between 10 and -10. Stored as int32_t because of previous calculations
long long compensation = total * PCSCALEVAL;
Serial.printf("compensation: %ld. total: %d. PCSCALEVAL: %d\r\n", compensation, total, PCSCALEVAL);

output: “compensation: 100000000. total: 1000000000. PCSCALEVAL: 0”

It looks like the printf parameters are being swapped and the calculation is not correct.

How should I resolve this?

Thanks


#2

Is there a certain reason why you are using a ‘long long’ type instead of ‘int32_t’? As the photon’s processor does not exceed 32bits we should expect undefined behaviour when using types meant for 64bit systems should we not?


#3

I’m being an idiot, thanks for the sanity check.

I was using long long as I mistakenly thought that 1000000000 would not fit in an int32 but it does.

Cheers


#4

Hi,

I was wrong about the range of my total variable and it can actually go between -99 and 99, this will be multiplied by PCSCALEVAL so I need to be able to use numbers between -9900000000 and 9900000000. These will not fit in the int32_t data type.

Is there an easy way to use large numbers by splitting them up or should I accept a loss in resolution by reducing the size of PCSCALEVAL by a factor of 10.

Cheers


#5

@joe4465, you could do your calculations with float or double and convert back to integer when necessary. Is there any reason you need do to everything with integers?


#6

Just speed, as I understand calculations to be much slower when using floating numbers rather than integers.

I have to perform this calculation (along with lots of others) 1024 times every 100ms.

Thanks


#7

If you want speed you might consider downscaling.
If you have a decadic factor, just drop the zeros completely. Just use a fitting unit.

Going long long would just counteract your speed intention as well.
Try to keep it within int32_t/uin32_t.

But what kind of calculations do you intend to perform?
Can you precalc factors?


#8

Hi @ScruffR,

I precalc pixC and scale

I then calculate the below 4 times every 100ms.

tA = (ptat * ptatGrad) + ptatOff

I then calculate the below 1024 times every 100ms.

Equations:

pComp1 = data - ((thGrad * tA) / scale) - thOff
pComp2 = pComp1 - elOff
pComp3 = (pComp2 * 100000000) / pixC

Code:

int32_t total = -_thGrad[pixelIndex]; //_thGrad is int16_t
Serial.printf("Step 1. total: %d, _thGrad: %d\r\n", total, _thGrad[pixelIndex]);

total *= tA; //tA is int16_t
Serial.printf("Step 2. total: %d. tA: %d\r\n", total, tA);

total /= _scale; //_scale is int32_t, always equals 2^20
Serial.printf("Step 3. total: %d. scale: %d\r\n", total, _scale);

total  -= _thOff[pixelIndex]; //_thOff is int16_t
Serial.printf("Step 4. total: %d. _thOff: %d\r\n", total, _thOff[pixelIndex]);

total -= elOff; //elOff is uint16_t
Serial.printf("Step 5. total: %d. elOff: %d\r\n", total, elOff);

total += data; //data is uint16_t
Serial.printf("Step 6. total: %d. data1 %d\r\n", total, data);

float compensation = (float)total * PCSCALEVAL; //TODO remove float somehow - I can reduce PCSCALEVAL by a factor of 10 but this reduces resolution.
Serial.printf("Step 7. compensation: %f. total: %d. PCSCALEVAL: %d\r\n", compensation, total, PCSCALEVAL);

total = compensation / _pixC[pixelIndex]; //_pixC is int32_t
Serial.printf("Step 8. total: %d. compensation: %d.  _pixC: %d\r\n", total, compensation, _pixC[pixelIndex]);

Thanks everyone for your help!

EDIT: Just wanted to clarify its the compensation line causing difficulty as 99 * 100000000 does not fit into an int32


#9

If _scale really always is 2^20 you could use total >>= 20;


#10

Thanks that’s cool.

_scale is always 2^someInteger where someInteger is read from EEPROM on init so I could just load someInteger and then do total>>=someInteger


#11

Yup!

BTW: What’s the range for _pixC[pixelIndex]?

Are you actually loosing information content (as in theory of information) or are you just assuming reduced resolution?

Just because you get a result with many digits, it does not necessarily mean that it is more accurate than one with less (theory of mathematical significance)


#12

@ScruffR the pixelIndex is 0 - 1024 and the value _pixC can be anywhere from 3E+6 to 7E+8.

The manufacturer told me reducing PCSCALEVAL would reduce resolution but I’m not sure whether reducing by a factor of 10 would really have any noticable difference. This also has a knock on affect on the formula used to calculate pixC which is the sensitivity coefficients for each pixel.

Cheers


#13

If you have no credible data to judge the effect, you might want to perform some empirical tests.

For one see the timing impact of using float/double vs. int
For precission test float/double vs. int again but with “relaxed” timing.

Based on these results you can judge if you need to walk the extra mile or not.

I’d guess with _pixC being in the range of 3E+6 you’ll see a lot of noise in the least siginificant digits, relativizing the significance.


#14

Ok good idea. Then I can use float if time is not an issue or int32 if it is and the resolution is good enough to allow me to remove a factor of 10.

Out of interest are there any methods used to store large numbers for situations like this?

Cheers


#15

Im guessing one possibility would be to define a structure/bitfield with multiple fields which together represent your large number. Then add getters/setters and overload the operators with corresponding assembly code which correctly adds/subtracts/… a numerical parameter from the structure’s fields. Factorization should however usually be enough for simpler applications.

struct emu_uint64 { // Emulated 64bit unsigned integer
    uint32_t hi;
    uint32_t lo;

    operator+(const uint32_t val) {
        ___asm___(...);
    }

    operator+(const emu_uint64 val) {
         ___asm___(...);
    }
}

#16

My guess is that the sprintf is not handling the 64-bt datatype. %ld means long not long long.

To print, I see you have 2 options:

  1. write your own routine to print the long long value
  2. convert to double and print using sprintf
  3. print out in hex, first the upper 32-bits, then the lower 32.

I hope that helps.


64 bit integer types and printing
#17

Thanks, is long long a valid 64 bit type on the platform then?


#18

Yes, it’s supported by the compiler. Similar to how it is on the Arduino Uno, even thought the MCU is only 8-bit, the compiler supports 32/64 bit operations in software.

Here, the MCU is 32-bit, and the compiler provides 64-bit operations in software.

The trap is that not all C runtime functions are equipped to deal with 64-bits, sprintf being one example.


#19

Update:
Nevermind, my own stupidity :flushed:
I should have written

    ll = (uint64_t)1 << b;

I should know better :pensive:


Outdated original post (code corrected tho’):
Sorry @mdma for resurrecting this thread, but in connection with another question I came up with this test code that does suggest that uint64_t (aka unsigned long long) is not fully supported - or I’m missing something :blush:

uint64_t ll = 0;

void setup()
{
    pinMode(D7, OUTPUT);
    Particle.function("shift", doShift);
}

void loop()
{
  digitalWrite(D7, ll ? HIGH : LOW);
}

int doShift(String bits)
{
    int b = bits.toInt();
    // ll = 1 << b; // <-- mistake: 1 is an int so it's an int shift
    // this works as expected
    ll = (uint64_t)1 << b; 
    return b;
}

I tried to avoid involving any “function” in the 64bit operations but only basic C operations and operators.
Hence the otherwise superfluous ternary operator to explicitly pass HIGH/LOW.

Once I pass any bit number greater than 31 the LED goes off.
How come, if 64bit is supported and how would we know what works and what not if not even this one works?


#20

Hi Guys,

Great thread here. I’m noticing it hasn’t moved since the middle of last year. Has anyone had success with an alternative approach? I’ve got some decoding to do on a custom EAN13 barcode which is 13 decimal digits long (essentially a 40-bit number) that requires some bit-bashing operations to extract fields from within the barcode. It comes in from the reader as an ASCII string, but I need to convert it to an integer representation to work on it.

@ScruffR, your last post was a little confusing. Were you implying that the code works after you cast the 1 to a uint64_t?

Just a little unclear, and I don’t want to go on another red-bull-fueled coding session only to discover my approach is dead broken.