## Problem with fixed point math

17 posts / 0 new
Author
Message

Hi, I have a problem with representing a negative number, like -0.91 using fixed point math?

I read the tutorial for the fixed point math from http://www.embedded.com/columns/15201575?_requestid=4559 but didn't understand how to do that.

I need to represent numbers from -1:1 with step 0.0001.
Then I use 2 bits for signed integer and 14 bits for fractional.

typedef union FIXED2_14tag{
S16 full;
struct part2_14tag{
U16 fraction: 14;
S16 integer: 2;
}part;
}FIXED2_14;

U-unsigned, S-signed

and from modified his example:

#define FIXED2_14CONST(A,B) (S16)((A<<14)+( (A<0 ? (B-0.000030517578125):(B+0.000030517578125))*16384 ))

This works ok for number -1.9999 to -1.0000 and from 0 to 1.9999, but can't represent number from -0.9999 to -0.0001
I know the problem is in the next statement:

(A<0 ? ...

because A can be 1,0 or -1 and what if I have the number -0.34 ?
In this case A is -0 and have a problem, I'm getting positive number (0.34), how to solve this problem?

You really need the fraction to be signed and the exponent unsigned. Or some other combinations like both parts being signed.

I have always done fixed point by hand. e.g. I work in millimetres rather than metres.

David.

Both the fraction, AND integer parts are signed, and treated as a single large integer.

Remember that with fixed point, all you are doing is scaling up the rational number by some power of two multiple, thus placing the decimal point at some binary boundary. [in your case 2^14 or 16384]

Writing code is like having sex.... make one little mistake, and you're supporting it for life.

slavko wrote:
Hi, I have a problem with representing a negative number, like -0.91 using fixed point math?
I haven't dug through those macros, but the bit patterns you need to achieve are as follows:
decimal   fixed point
==        ==
+2.0000   unrepresentable
+1.9999   01(.)11111111111111
...
+1.0001   01(.)00000000000001
+1.0000   01(.)00000000000000
+0.9999   00(.)11111111111111
...
+0.0001   00(.)00000000000001
+0.0000   00(.)00000000000000
-0.0001   11(.)11111111111111
...
-0.9999   11(.)00000000000001
-1.0000   11(.)00000000000000
-1.0001   10(.)11111111111111
...
-1.9999   10(.)00000000000001
-2.0000   10(.)00000000000000

Of course, the decimal representations are not exact; by "+0.9999" I simply mean "the largest representable number that is less than 1.0000" for example. To convert a float to this format, you simply multiply it by 2^13 and then round to the nearest integer. For example, -0.91 x 2^13 = -7545 (to the nearest integer), which is 1110001010000111 in your chosen 2.14 format.

As far as I can see, most of the complication in the macro is trickery to make the preprocessor perform rounding, but even so you won't achieve steps of exactly 0.0001 (not even if you use floating point).

As an alternative to fixed point of this type, you might also find it useful to think in integer multiples of a smaller unit, rather than fractions of a bigger one. For example, if your number represents a current in Amps, then simply work in mA or uA instead.

Christopher Hicks
==

slavko wrote:
I need to represent numbers from -1:1 with step 0.0001.
Use a signed int.
With an implicit division by 10,000,
you can exactly represent numbers in the range
-3.2768 to 3.2767 in steps of 0.0001 .
To compare with a float, multiply the float
by 10000f before the comparison.

Edit: Lee's correction included

Iluvatar is the better part of Valar.

Last Edited: Mon. Jun 1, 2009 - 06:19 PM

Quote:

you can exactly represent numbers in the range
-6.5536 to 6.5535 in steps of 0.0001 .

Well, kinda, but it will still cover the range. ;) Try -3.2768 to 32767.

Lee

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

@ cmhicks
yes, I know that I need to get that, but the problem is with macro, I think I found solution to use only numbers from 0 to 1.9999....

@theusch @skeeve
I don't know for that solution, I didn't try it, but I will.

You mentioned -1 to 1 but you also talk about 1.999. That would make the range {2>x>-2} or, as some write (2,-2). but NOT {2=>x=>-2} or [2,-2].

I think that you confuse yourself by thinking of the whole part and the fractional part separately. Instead, think of the entire number, 1.999 or -1.999.

Jim

Until Black Lives Matter, we do not have "All Lives Matter"!

Jim, the problem is with numbers between -0.9999 and -0.0001.

In the macro he test is A<0 or A>0 (A is whole part of number) and then determine sign (A can be -1, 0 or 1), but what if the number is -0.45? It is negative number, but A isn't less than 0, since A is 0 and I get the positive number 0.45.

(A<0 ? (for negative A):(for pozitive A))*16384 ))

Persaonally I don't like that macro... I don't see the point in having A & B, as it's using floating point anyway... why not just write the constant as a floating point value to begin with.

#define FIXED2_14CONST(A) (S16)((A<0 ? (A-0.000030517578125):(A+0.000030517578125))*16384)
.
.
.
myFixVar.full = FIXED2_14CONST(-0.9);

Writing code is like having sex.... make one little mistake, and you're supporting it for life.

slavko wrote:
I read the tutorial for the fixed point math from http://www.embedded.com/columns/15201575?_requestid=4559 but didn't understand how to do that.
I read enough to know that I don't want to study it too hard.
Look up scaled integers. Dissecting numbers is not required.

Iluvatar is the better part of Valar.

skeeve wrote:
slavko wrote:
I read the tutorial for the fixed point math from http://www.embedded.com/columns/15201575?_requestid=4559 but didn't understand how to do that.
I read enough to know that I don't want to study it too hard.
Look up scaled integers. Dissecting numbers is not required.

Depends on the end use. (Base 10) Scaled integers are easier to grasp in your head, and often give the desired resolution stepping (as we tend to think base 10) Having said that, they cannot be as easily or quickly converted back into their respective integer and fractional parts as can be done with fixed point numbers. Note that fixed point is just a special form of scaled integer... it is a Base 2 Scaled integer.

Fixed point numbers really come into their own when you need only the integer portion for the end result, but you need the fractional precision during processing.

(Base 10) Scaled integers are great as you can scale the precision to exactly what you need. They also tend to be easier to display, as the normal integer display routines can be quickly adjusted to accommodate the decimal.

Writing code is like having sex.... make one little mistake, and you're supporting it for life.

I know what you mean. Sometimes base 10 values are easy, for example I have a 16-bit timer counting milliseconds so every main loop I get a difference how many milliseconds has passed since last main loop. Increment can be 0-65535 so that is up to 65.535 seconds. Quite a lot and more than I need. I started to think about using 0.1ms units (100us) which would allow me to count up to 6.5535 seconds.

Then when a lot of the code was already using millisecond values, I thought I need to keep 1ms time and 0.1ms time units separate, but as it is easy to look for 1ms increment from 100us units by just dividing by 10, the fraction needs to be saved so it is not truncated out. Having the microcontroller do one division by 10 and one modulo by 10 every mainloop started to think stupid, I thought there has got to be easier way.

One way was not to think of one tenth of milliseconds but one eighth or one sixteenth of a millisecond. A microcontroller can then do the division by shifting and modulo by anding.

Or, I could keep the sub-milliseconds accumulating by some other way, which is smarter. But I did not yet need other than 1ms timers, so I just dumped the idea.

I have used fixed point numbers in my projects, you can use these macros to represent numbers in [-128, 128), in steps of 1/256:

// 8:8 Fixed Point Math
typedef signed int fixed;
#define int2fix(a)   (((int)(a))<<8) //Convert char to fix
#define fix2int(a)   ((signed char)((a)>>8)) //Convert fix to char
#define float2fix(a) ((int)((a)*256.0)) //Convert float to fix
#define fix2float(a) ((float)(a)/256.0) //Convert fix to float
#define multfix(a,b) ((int)((((long)(a))*((long)(b)))>>8)) //multiply
#define divfix(a,b)  ((int)((((long)(a))<<8)/((long)(b)))) //divide

The hibyte of the fixed number represents the integer part, the low byte represents the fractional part.

If you want to see an example on how to use them, take a look at my multikitb project, in the projects area.

ganzziani wrote:
I have used fixed point numbers in my projects, you can use these macros to represent numbers in [-128, 128)

not quite... your range is -128.000 to +127.99609375 (one lsb less than 128 on the positive side)

but the macros are good... pretty much the same as the ones I use.

Writing code is like having sex.... make one little mistake, and you're supporting it for life.