EmbeddedRelated.com
Forums
The 2024 Embedded Online Conference

Floating point vs fixed arithmetics (signed 64-bit)

Started by kishor March 26, 2012
	Hi friends,
	I am working on stellaris LM3s6965 (cortex-m3) & Keil 4.20 for data
acquisition. ADC
	is signed 24-bit.

	To perform software Gain calibration I have two options,

	1. 64-bit fixed width arithmetic
		uint16_t Gain;		// 0x8000 means gain is 1
		int32_t ADC_Reading;		// It contains 24-bit signed integer ADC
reading

		ADC_Reading = ((int64_t)ADC_Reading * Gain) / 0x8000;           //
Gain calibration

		// As multiplication of signed 24-bit & unsigned 16-bit will not fit
into 32-bit variable
		// I typecast it to int64_t.

	2. Single precision Float
		float Gain;
		int32_t ADC_Reading;		// It contains 24-bit signed integer ADC
reading

		ADC_Reading = ADC_Reading * Gain;                     // Gain
calibration

		Which is better for performance wise.

		Thanks,
		Kishore.
Op Mon, 26 Mar 2012 11:22:21 +0200 schreef kishor <kiishor@gmail.com>:
> Hi friends, > I am working on stellaris LM3s6965 (cortex-m3) & Keil 4.20 for data > acquisition. ADC is signed 24-bit. > > To perform software Gain calibration I have two options, > > 1. 64-bit fixed width arithmetic > ADC_Reading = ((int64_t)ADC_Reading * Gain) / 0x8000; > 2. Single precision Float > ADC_Reading = ADC_Reading * Gain; > > Which is better for performance wise[?]
An (u)int64_t multiplication is always faster than a float multiplication, assuming you don't have a hardware FPU. Also, if you can deal with some loss of precision, you can pre-divide both operands enough to be able to use 32-bit multiplication. -- Gemaakt met Opera's revolutionaire e-mailprogramma: http://www.opera.com/mail/ (Remove the obvious prefix to reply privately.)
On 03/26/2012 11:22 AM, kishor wrote:
> Hi friends, > I am working on stellaris LM3s6965 (cortex-m3)& Keil 4.20 for data > acquisition. ADC > is signed 24-bit. > > To perform software Gain calibration I have two options, > > 1. 64-bit fixed width arithmetic > uint16_t Gain; // 0x8000 means gain is 1 > int32_t ADC_Reading; // It contains 24-bit signed integer ADC > reading > > ADC_Reading = ((int64_t)ADC_Reading * Gain) / 0x8000; // > Gain calibration
Cortex-M3 has a 32x32->64 bit multiply instruction, so if your compiler is smart enough, it might use that. Check the generated assembly output. If not, write your own assembly version.
On 26/03/2012 13:14, Arlet Ottens wrote:
> On 03/26/2012 11:22 AM, kishor wrote: >> Hi friends, >> I am working on stellaris LM3s6965 (cortex-m3)& Keil 4.20 for data >> acquisition. ADC >> is signed 24-bit. >> >> To perform software Gain calibration I have two options, >> >> 1. 64-bit fixed width arithmetic >> uint16_t Gain; // 0x8000 means gain is 1 >> int32_t ADC_Reading; // It contains 24-bit signed integer ADC >> reading >> >> ADC_Reading = ((int64_t)ADC_Reading * Gain) / 0x8000; // >> Gain calibration > > Cortex-M3 has a 32x32->64 bit multiply instruction, so if your compiler > is smart enough, it might use that. Check the generated assembly output. > > If not, write your own assembly version.
Unless you require the absolutely fastest performance (and someone asking the original question clearly is not - or he would already have found the answer), do not write your own assembly code. It's just pointless optimisation for optimisation's sake. By all means, look at the generated assembly and see if it uses the ideal instruction. If it doesn't, then file a report or support request with the compiler supplier if you want. Don't do inline assembly unless you really have a reason for it, especially if you are not used to it.
Thanks for reply,

Compiler has generated "UMULL" instruction, (32-bit * 32-bit)
As it is signed multiplication it generated another three instructions.

Assembly listing is as below,

 r1 - ADC_Reading (signed)
 r2 - Gain (unsigned)

      UMULL    r0,r5,r1,r2            ; unsigned multiply 32 * 32
      ASRS     r3,r1,#31               ; Arithmetic Shift Right
      MLA      r2,r3,r2,r5              ; Multiply & Accumulate 
      MLA      r1,r1,r12,r2            ; Multiply & Accumulate 

      MOV      r2,#0x8000
      MOV      r3,r12

      BL       __aeabi_ldivmod     ; 64-bits divider function

So multiplication is not a big deal. signed 64-bit divide takes time.

So still is it better than float?

Thanks,
Kishore.



>-----< kishor > > So multiplication is not a big deal. signed 64-bit divide takes time.
You don't need the division. You need to shift down 31 bits to get back to 32 bits. Perhaps the ALU can do to all for you in one step. Check the compiler for a built-in function for 32-bit signed fractional multiplication. Read up on fractional arithmetic. -- Fredrik &Ouml;stman
On Monday, March 26, 2012 6:08:00 PM UTC+5:30, Fredrik =D6stman wrote:

> You don't need the division. You need to shift down 31 bits to get back=
=20
> to 32 bits.
I don't understand your point. In signed division we can't shift down bits = simply.
> Perhaps the ALU can do to all for you in one step. Check the compiler for=
=20
> a built-in function for 32-bit signed fractional multiplication. >=20 > Read up on fractional arithmetic.
Is there other method which avoids 64-bit division? Kishore.
On 03/26/2012 02:24 PM, kishor wrote:
> Thanks for reply, > > Compiler has generated "UMULL" instruction, (32-bit * 32-bit) > As it is signed multiplication it generated another three instructions. > > Assembly listing is as below, > > r1 - ADC_Reading (signed) > r2 - Gain (unsigned) > > UMULL r0,r5,r1,r2 ; unsigned multiply 32 * 32 > ASRS r3,r1,#31 ; Arithmetic Shift Right > MLA r2,r3,r2,r5 ; Multiply& Accumulate > MLA r1,r1,r12,r2 ; Multiply& Accumulate > > MOV r2,#0x8000 > MOV r3,r12 > > BL __aeabi_ldivmod ; 64-bits divider function > > So multiplication is not a big deal. signed 64-bit divide takes time. > > So still is it better than float?
Try doing a >> 15 instead of a / 0x8000 in your C code.
On 26/03/2012 15:33, kishor wrote:
> On Monday, March 26, 2012 6:08:00 PM UTC+5:30, Fredrik &#4294967295;stman wrote: > >> You don't need the division. You need to shift down 31 bits to get >> back to 32 bits. > > I don't understand your point. In signed division we can't shift down > bits simply.
Correct. But the compiler should do the strength reduction for you - take note of the sign, do everything unsigned, then restore the sign. If it doesn't, then check your optimisation settings and/or complain to the supplier.
> >> Perhaps the ALU can do to all for you in one step. Check the >> compiler for a built-in function for 32-bit signed fractional >> multiplication. >> >> Read up on fractional arithmetic. > > Is there other method which avoids 64-bit division? >
Yes - do everything unsigned. First think if the incoming data really is signed - in most cases it is not. But if you have signed data (say from a differential input), first note the sign then convert to a positive value if needed. Then do your scaling and division (and if the compiler can't convert an unsigned divide by 0x8000 to a shift, it's a poor compiler - and you can do the shift by hand). Then restore the sign.
On 03/26/2012 03:33 PM, kishor wrote:
> On Monday, March 26, 2012 6:08:00 PM UTC+5:30, Fredrik &#4294967295;stman wrote: > >> You don't need the division. You need to shift down 31 bits to get back >> to 32 bits. > > I don't understand your point. In signed division we can't shift down bits simply.
The difference is at most 1 LSB due to rounding differences, which is probably less than your ADC noise. You can make the difference even smaller by using a 32 bit gain variable. You could also find out if ADC value is negative, reverse the sign, perform unsigned arithmetic, and reverse the sign of the result.

The 2024 Embedded Online Conference