0

There is C a array like uint8_t a[8] = {1, 2, 3, 4, 5, 6, 7, 8}; and constant c=100.

I'd like to do this:

for(i = 0;i < 8; i++) {
  a[i] = (a[i] * c) >> 8;
}

However as it's only 8bit data I wonder if there is a trick to somehow multiply and scale back to 8bit more elements at once with a 32 bit MCU. (there are no bulit-in vector operations)

EDIT: Changed the word "normalize" to "scale to 8bit"

  • 1
    Which MCU are you compiling for? – tenfour Oct 03 '22 at 09:39
  • Are you sure you mean "normalise"? Multiplying all values by a constant (and then dividing by 256) is not "normalising" (in the normal sense of the word). – Fe2O3 Oct 03 '22 at 09:50
  • It should be platform and compiler agnostic. I mean normalize to be in the 0..255 range again. – Gábor Kiss-Vámosi Oct 04 '22 at 10:27
  • The correct terms rather seem to be _truncate_ (drop ms bytes) or _mask_ (keep one particular part of the data). Or if you wish to divide the result by 256, then say so explicitly. – Lundin Oct 04 '22 at 10:47
  • platform and compiler agnostic prevent to exploit MCU/CPU capabilities like SIMD instructions ... – Spektre Oct 05 '22 at 06:35

1 Answers1

4

You can multiply two array elements at once by giving each one 16 bits of the 32-bit value.

However, this will only work if each a[i] * c is guaranteed not to overflow 16 bits (i.e. if the value assigned to a[i] in your code is no more than 255). If it does overflow then some values will be off by 1 due to the carry being propagated.

I'm also assuming that c fits in 16 bits - if it doesn't then you can truncate it to 16 bits without changing your original code's result.

for (i = 0; i < 8; i += 2) {
    uint32_t x = (((uint32_t)a[i+1] << 16) | a[i]) * c;
    a[i] = (x >> 8) & 0xFF;
    a[i+1] = x >> 24;
}
interjay
  • 107,303
  • 21
  • 270
  • 254
  • How is this "normalising" the values of the array? – Fe2O3 Oct 03 '22 at 10:24
  • @Fe2O3 What do you mean? This should do what the original code does (with the above caveat), whether you choose to call it "normalizing" is irrelevant. – interjay Oct 03 '22 at 10:34
  • To "normalise" is not simply multiplying by one arbitrary constant and dividing by another arbitrary constant. What the OP shows and what you've shown would be "integer arithmetic scaling and rounding" (aka truncation)... Do you know what "normalising" grades (or any dataset of values) is all about? – Fe2O3 Oct 03 '22 at 10:38
  • 1
    @Fe2O3 Seems like your issue is with OP's phrasing then and not with this answer? Besides, the word "normalization" has many meanings and I don't think its usage is necessarily incorrect here (in the context of fixed-point multiplication, which is what OP's code does). – interjay Oct 03 '22 at 10:54
  • 1
    @Fe2O3 Op's code does an operation which is often useful. This answer provides a faster way to do the same. Your comments are not constructive or helpful in any way. – interjay Oct 03 '22 at 11:06
  • Normalize probably wasn't fully correct, although I think it's commonly (mis)used this way. Anyway, I update the question. I'll test the suggested algorithm's performance and report back. – Gábor Kiss-Vámosi Oct 04 '22 at 10:46
  • With -O3 on a Cortex-M7 200 MHz MCU the suggested method is only 1% faster. – Gábor Kiss-Vámosi Oct 07 '22 at 16:28