Advertisements

Arduino vs. Significant Figures: Preliminary 64-bit Fixed Point Exercise

Although it’s not advertised, the Arduino / AVR compiler mostly does the right thing with long long = uint64_t variables: add & subtract work fine, but multiplication & division discard anything that doesn’t fit into 64 bits. Fitting a 32 bit integer and a 32 bit fraction into such a thing should eliminate (most) problems with significant figures.

The general idea is to set up a struct giving access to the two 32 bit halves for direct manipulation, then overlay / union them with a single 64 bit integer for arithmetic purposes:

struct ll_s {
  uint32_t low;
  uint32_t high;
};

union ll_u {
  uint64_t ll_64;
  struct ll_s ll_32;
};

Of course, the integer part still falls one bit shy of holding 2³². At the cost of one bit’s worth of resolution, you can still compute 2³² / 125×10⁶ by pre-dividing each quantity by 2:

2^63 = [80000000 00000000]
2^63 / 125/2 M = [00000022 5c17d04d]

The low-order digit should be 0xe, not 0xd, but I think that’s survivable.

Unfortunately, printf doesn’t handle 64 bit quantities, necessitating some awkward conversion routines. “Printing” to a string seems the least awful method, as I’ll eventually squirt the strings to a display, not send them to the serial port:

void PrintFractionLL(char *pBuffer,uint64_t *pLL) {
  uint64_t Fraction;

  Fraction = (uint32_t)*pLL;                      // copy 32 fraction bits, high order = 0
  Fraction *= ONEGIG;                             // times 10^9 for conversion
  Fraction >>= 32;                                // align integer part in low long
  sprintf(pBuffer,"%09lu",(uint32_t)Fraction);    // convert low long to decimal
}

void PrintIntegerLL(char *pBuffer,uint64_t *pLL) {
  sprintf(pBuffer,"%lu",*((uint32_t *)pLL+1));
}

void PrintDecimalLL(char *pBuffer,uint64_t *pLL) {
  PrintIntegerLL(pBuffer,pLL);
  pBuffer += strlen(pBuffer);       // pointer to end of integer part
  *pBuffer++ = '.';                 // drop in the decimal point, tick pointer
  PrintFractionLL(pBuffer,pLL);
}

The result seems nearly indistinguishable from the Right Answer:

Integer:  34
Fraction: 359738367
Decimal: 34.359738367

This whole mess has a bunch of rough edges, but it looks promising. The code coalesced while fiddling around, so the union notation didn’t get much love at first.

The Arduino source code as a GitHub Gist:

Advertisements

,

  1. #1 by William Rutiser on 2017-05-24 - 10:35

    What tool chain do use for Arduino work?
    You have inspired me to at least find and dump out the contents of a box of Arduino stuff from Dec 2010.

    • #2 by Ed on 2017-05-24 - 19:09

      Not much of a chain: the plain old Arduino IDE set to “use external editor” on the landscape monitor and KDE fullscreen on the portrait monitor. Save the file, click upload, and away it goes.

      The bone-stock IDE lets me get away with columns / article minus all the verbiage explaining how to squirt programs into microcontrollers.

  1. Arduino vs. Significant Figures: Useful 64-bit Fixed Point | The Smell of Molten Projects in the Morning