简体   繁体   中英

What is the fastest way to multiply a 16-bit integer with a double?

On an 8-bit micro controller I would like to do the following:

16bit_integer = another_16bit_integer * 0.997;

with the least possible number of instructions.

How about integer arithmetic in 32 bits?

16bit_integer = (int16_t) (another_16bit_integer * (int32_t) 997 / 1000);

32 bits will be enough to store (INT16_MAX × 997), do the sum on values 1000 times larger then divide back to your 16 bit scale.

Bit shifts are usually very fast:

y = 0xFF3B * (int32_t) x >> 16;

This is probably better written as:

y = (0.997 * 0x10000) * (int32_t)x >> 16;

A good compiler will generate equivalent output.

If your integers are signed, the constants should be changed to 0x8000 and 15.

You probably meant to have some rounding in there, rather than truncating the result to an integer, otherwise the purpose of the operation is really limited.

But since you asked the question with that specific formula, it brought to mind that your result set is really coarse. For the first 333 numbers, the result is: another_16bit_integer-1. You can approximate it (maybe even exactly, when not performed in my head) with something like:

16bit_integer = another_16bit_integer - 1 - (another_16bit_integer/334);

edit: unsigned int, and you handle 0 on your own.

On my platform ( Atmel AVR 8-bit micro-controller, running gcc )

16bit_integer = another_16bit_integer * 0.997;

Takes about 26 instructions.

16bit_integer = (int16_t) (another_16bit_integer * (int32_t) 997 / 1000);

Takes about 25 instructions.

Here is a very fast way to do this operation:

a = b * 0.99609375;

It's similar to what you want, but it's much faster.

a  = b;
a -= b>>8;

Or even faster using a trick that only works on little endian systems, like the PIC.

a  = b;
a -= *((int8*)((&b)+1));

Off the top of my head, this comes down to the following assembler on a PIC18:

; a = b
MOVFF 0xc4, 0xc2
NOP
MOVFF 0xc5, 0xc3
NOP

; a -= *((int8*)((&b)+1));
MOVF  0xc5, w
SUBWF 0xc2, f
BTFSC STATUS, C
DECF  0xc

预先计算的查找表:

16bit_integer = products[another_16bit_integer];

Precomputed lookup table:

16bit_integer = products[another_16bit_integer];

That's not going to work so good on the AVR, the 16bit address space is going to be exhausted.

Since you are using an 8 bit processor, you can probably only handle 16 bit results, not 32 bit results. To reduce 16 bit overflow issues I would restate the formula like this:

result16 = operand16 - (operand16 * 3)/1000

This would give accurate results for unsigned integers up to 21845, or signed integers up to 10922. I am assuming the the processor can do 16 bit integer division. If you cannot then you need to do the division the hard way. Multiplying by 3 can be done by simple shifts & adds, if no multiply instruction exists or if multiplication only works with 8 bit operands.

Without knowing the exact microprocessor it is impossible to determine how long such a calculation would take.

On my platform ( Atmel AVR 8-bit micro-controller, running gcc )

16bit_integer = another_16bit_integer * 0.997;

Takes about 26 instructions.

16bit_integer = (int16_t) (another_16bit_integer * (int32_t) 997 / 1000);

Takes about 25 instructions.

The Atmel AVR is a RISC chip, so counting instructions is a valid comparison.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM