0

i want to multiple values by scale factor on microcontroler, and i cant use float values. For example, once i've got value 170 which is multiply by scale factor ex. 0.00065, so result is 0.1105 and i only need 0.11. I've got values form -500 to +500. I know that it's something about fixed point but i'cant figure out how to solve it. Thans for help

Matt
  • 11
  • 6

2 Answers2

1

If your input is an integer, and only the output requires to be fixed point, it's fairly simple.

Lets use 16.16 fixed point. This means you have 16 bits of fraction, and 16 bits of integer. To convert an integer to fixed point, just shift left by 16 bits (or multiply by 65536).

Your input is 170, or 0xAA. So the fixed-point representation would be 0xAA0000.

You want to multiply by 0.00065, which would be 0x002b.

To multiply you need to either multiply the numbers together and then cancel out one of the scales (both inputs are scaled, so the result will be scaled twice), or cancel one of the scales before multiplying.

0xaa0000 * 0x2b = 0x1c8e0000
0x1c8e0000 >> 16 = 0x1c8e

However note that this could overflow with a larger input. If you tried to use 500, it would produce a larger than 32-bit value.

If your input is an integer, you could just leave it unshifted. Lets do that:

0xaa * 0x2b = 0x1c8e

Same answer, but no shifting. However your input obviously can only be an integer.

Either way, 0x1c8e is your answer, but expressed in 65536ths. If you take that number in decimal (7310) and divide by 65536, you'll get 0.11154..., which is an approximation of the answer you want.

You can choose any fraction/integer split you like with fixed point, and you could even mix and match. You just have to take into account where the split is when you perform any mathematical operation, usually shifting either the inputs or the outputs appropriately. The main problem to deal with is picking a format which doesn't overflow or underflow for the operations you want to do.

To print the values out, you just need a little bit of integer manipulation.

Lets take the value 0xaa1c8e. First, take the integer part by shifting by 16. That gives 0xaa.

Now take the fraction, which is 0x1c8e. Multiply by an appropriate amount for the digits you want. Lets just multiply by 100, for two decimal digits. That gives 0xb2778. Shift that back by 16, which leaves just 0xb.

If you printf with the string: "%d.%02d", 0xaa, 0xb, you'll get "170.11", which is correct.

Here's a rough and ready example:

int a = (0.00065 * 65536 + 0.5); // Convert the float value to fixed point, with rounding.
int b = 170;

int m = b * a;

bool n = false;
if(m < 0) {m = -m; n=true;}
int i_part = m >> 16;
int f_part = m & 0xffff;
f_part *= 100;
f_part >>= 16;

printf("%s%d.%02d\n",n?"-":"",i_part,f_part);
JasonD
  • 16,464
  • 2
  • 29
  • 44
  • thanks for answer, only thing left me to do is print it on screen. i'm using sprintf function to do it so how can i put into string value 0.xx .. ? and next 1.5x – Matt Dec 18 '12 at 16:47
  • If you really need to print it in decimal form, without float support you'll need a custom function to format it. – JasonD Dec 18 '12 at 16:51
  • ugh... i think there isnt any function to do it .. ? – Matt Dec 18 '12 at 16:54
  • its not too tricky. If you only want a couple of digits, multiply your number by 100, and then divide by 65536 (which is a shift). You just need to print the integer with a decimal point in the right place. – JasonD Dec 18 '12 at 16:59
  • i just tested your proposition. It's good but when i got b=-170 i should get -0.11 using your code it's -1.88. i tried this int main(void) { short int a,b,d,c; a=-300; // c=((a*100000)*65)/1000000; b=c/10000; d=c%10000; printf("try = %d",c); printf(" try = %d.%02d",b,d); but i got 0.-**** or when i a i greater and it's got minus sign sometimes i got possitive value – Matt Dec 18 '12 at 20:46
  • Yeah, that wasn't a complete implementation, but just the general idea. – JasonD Dec 18 '12 at 20:57
  • I've updated my little example to handle negatives - it's not a problem with the maths, just with the printing. I'm not sure about your version - some of those numbers look a little large for a normal sized short. – JasonD Dec 18 '12 at 21:09
  • thanks for answering. Your option imo it's the best to get first value before coma.Now it's all about value after coma.I'havent got idea how to solve it.In your code you first multiply f_part =m* 100;and next shift f_part >>= 16. So if the proper value should be ex. 1.152 after this operations it's 1.152, you shift all value not value which should be after coma. So, i tried find value after coma a=200 b=(a*65)%100000 result is correct0.13 but i'll need sometimes more precision ex a=50 b=(a*65)%100000 displayed result is 0.325, correct result should be 0.0325 So how display praperly the result – Matt Dec 19 '12 at 18:27
  • If you expect 0.0325, but print 0.325, then probably you're just missing the right format string while printing. – JasonD Dec 19 '12 at 18:41
  • printf("value = %d.%02d\n",c,d); - where c.d of value what's wrong? – Matt Dec 19 '12 at 18:59
  • If d is 4 digits, you need %04d to print it. – JasonD Dec 19 '12 at 19:23
  • it doesnt work, any change of %0xd doesnt work. Ex %01d still prints 0.325 – Matt Dec 19 '12 at 19:56
  • It only moves the value 0.325 to 0. 325 – Matt Dec 19 '12 at 20:02
  • Sounds like printf is broken, in which case you'll have to roll your own - but printing integers isn't hard. – JasonD Dec 19 '12 at 20:11
0

Represent your fixed point by 2 integers and use them both once you need to perform some operation. E.g., 0.11 = 11/100, so you can have two integer variables, numerator(11) and denominator(100) and they in conjunction represent a rational number corresponding to your value.

SomeWittyUsername
  • 18,025
  • 3
  • 42
  • 85