I need to read in a 16bit number from ADC and then modify it by a certain percentage. I searched but found no examples of anyone doing this, (I know, I know, keep reading)

I read that Floating point is slow and fixed point is better, but I just can't see how to do fixed point with the data I have since I need to send it over spi as the modified number when finished

ex:

Code:

int16 x;
float percent = .204; //20.4%
...

x = read_adc();
x = (x * percent) + x;
spi_write(x);

Maybe I am missing somthing but if I did this in fixed point wouldn't I have to do this:

Code:

int16 x;
int percent = 204;
...

x = read_adc();
x*= 10;
x = (x * percent) + x;
x/=10;
spi_write(x);

Which does not at all seem like a good idea. Is there any faster way add a percentage to a number ?

I don't think lookup table will work since I have over 8000 possible values coming in. Besides I think that would take more then the 32k I have for rom

But speed is VERY important.

Ideas ?

Guest

Actually,

Posted: Sat Mar 04, 2006 12:26 am

I'de like to know how to do this also.

I am was working on somthing like this awhile back. I used floating point but I had 100ms between samples so I really could do whatever I wanted. Did feel a little guilty using floating point

TtelmahGuest

Posted: Sat Mar 04, 2006 5:10 am

Normally, when working with integers like this, you keep the integer scaled. So (for instance), you might multiply the incoming number by 100, and then only divide it back at the end of whatever operations you want. For the simple scaling here, provided you are only multiplying, then staying with a float, is probably the best solution. A float multiplication, on a 40MHz PIC18, only takes typically 36uSec (remember these chips have a hardware 8*8 multiplier, which makes a big difference). There will be some overhead, converting back from float to integer, but the total from using int arithmetic is not likely to be much better. Where things improve a lot, is on things like division, where a float divide easily takes 4* the time of an int16 divide, and things like sin, where the time shoots up.
I'd suggest that the fastest solution, would be:

Code:

int16 x;
...

x = (int16)(read_adc() * 1.204);
spi_write(x);

Note the use of '1+percent', rather than multiplying, and then performing an addition. A float addition, actually takes longer than a multiply on a PIC18, and you are already performing a multiply, so why not do the two operations in one!. Also using a constant, may save a couple of I/O cycles.

Best Wishes

rberek

Joined: 10 Jan 2005Posts: 207Location: Ottawa, Canada

Posted: Sat Mar 04, 2006 7:39 am

You can also do a multiplication by 1.204 as a shift and add operation. It will not be exact but you can get it within 21 parts per million if you are using a 16-bit x value.

x * 1.204 is approximately equal to

x + x/8 + x/16 + x/64 + x/2048 + x/4096 + x/8192

or

x + x>>3 + x>>4 + x>>6 + x>>11 + x>>12 + x>>13

Actually this gives you x*1.203979 which should be close enough for most people.

I don't know how this works as far as speed goes, but I offer it as an alternative._________________The difference between genius and stupidity is that genius has its limits...

TtelmahGuest

Posted: Sat Mar 04, 2006 10:18 am

I suspect it'll be slower on the 18 chips, but might be better on the 16 chips (because of the lack of the hardware multiplier). You'd probably be better to shift a temporary store, otherwise there is a lot of 'repeat shifting' going on. Certainly in many cases, an approach like this is well worth considering.

Best Wishes

iso9001

Joined: 02 Dec 2003Posts: 262

Posted: Sat Mar 04, 2006 1:58 pm

Freakin sweet! I like both.

I feel rather dumb for forgeting to * 1.204 and not *.204+x. Don't know what I was thinking.

I know the 18F has the hardware multiply, but going off of CCS's chart in the help file it looks like my mulitply will take ~360 instructions like Ttelmah said. I need to do this twice in a row for two different numbers so I almost thinking 360x2 is near the time limit I have.

I really like the shifting. That seems to me like it would be less then ~350, but I have no idea yet.

I'll need to look closer at how you figured out the shift pattern. I don't get it but I only glanced at it for a second.

Wanted to say thanks, I'll try a bunch of different ways out and time them all

You do the same thing in decimal multiplication too, but you never think about it.

When you do fractions in binary, you only have available 1/2, 1/4, 1/8 etc to make up the decimal fraction. If you look at .204, it is made up of:

0.125 + 0.0625 + 0.15625 ....

you can come close to representing any decimal fraction as binary shifts, as long as the number you are shifting is large enough. Clearly you can't right-shift a 4-bit number very often before the shift results in a zero._________________The difference between genius and stupidity is that genius has its limits...

Mark

Joined: 07 Sep 2003Posts: 2838Location: Atlanta, GA

Posted: Sat Mar 04, 2006 3:46 pm

If your percentage is fixed at 20.4% then multiplying by 209 and dividing by 1024 will give you 20.41%. The divide by 1024 will be fast since its a binary number. If you can live with 20.3% then you could just multiply by 52 and truncate the last byte.

Holy GhostGuest

Back again

Posted: Sun Mar 05, 2006 7:39 pm

Wow, Nice to see that Mark has made a "comeback" to the forum.. Cheers..

Mark

Joined: 07 Sep 2003Posts: 2838Location: Atlanta, GA

Posted: Mon Mar 06, 2006 7:42 pm

I've been quite busy (and still am) at work and started shifting towards ARM7's which accounts for my lack of participation