HFOrangefish
HFOrangefish

Reputation: 268

C: Representing a fraction without floating points

I'm writing some code for an embedded system (MSP430) without hardware floating point support. Unfortunately, I will need to work with fractions in my code as I'm doing ranging, and a short-range sensor with a precision of 1m isn't a very good sensor.

I can do as much of the math as I need in ints, but by the end there are two values that I will definitely need to have fractions on; range and speed. Range will be a value between 2-500 (cm), while speed should be no higher than -10 to 10 (ms^-1). I am unsure how to represent them without floating point values, if it is possible. A simple way of rounding the fractions up or down would be best.

Some sample code I have:

voltage_difference_new = ((memval3_new - memval4_new)*3.3/4096); where memval3_new and memval4_new are ints, but voltage_difference_new is a float.

Please let me know if more information is needed. Or if there is a blindingly easy fix.

Upvotes: 3

Views: 746

Answers (1)

Clifford
Clifford

Reputation: 93476

You have rather answered your own question with the statement:

Range will be a value between 2-500 (cm),

Work in centimetre (or even millimetre) rather than metre units.

That said you don't need floating-point hardware to do floating point math; the compiler will support "soft" floating point and generate the code to perform floating point operations - it will be slower than hardware floating point or integer operations, but that may not be an issue in your application.

Nonetheless there are many reasons to avoid floating-point even with hardware support and it does not sound like your case for FP is particularly compelling, but it is hard to tell without seeing your code and a specific example. In 32 years of embedded systems development I have seldom resorted to FP even for trig, log, sqrt and digital signal processing.

A general method is to use a fixed point presentation. My earlier suggestion of using centimetres is an example of decimal fixed point, but for greater efficiency you should use binary fixed point. For example you might represent distance in 1/1024 metre units (giving > 1 mm precision). Because the fixed point is binary, all the necessary rescaling can be done with shifts rather than more expensive multiply/divide operations.

For example, say you have an 8 bit sensor generating linear output 0 to 255 corresponding to a real distance 0 to 0.5 metre.

#define Q10_SHIFT = 10 ; // 10 bits fractional (1/1024)
typedef int q10_t ;

#define ONE_METRE = (1 << Q10_SHIFT)
#define SENSOR_MAX = 255
#define RANGE_MAX = (ONE_METRE/2)

q10_t distance = read_sensor() * RANGE_MAX / SENSOR_MAX ;

distance is in Q10 fixed point representation. Performing addition and subtraction on such is normal integer arithmentic, multiply and divide require scaling:

int q10_add( q10_t a, q10_t b )
{
    return a + b ;
}

int q10_sub( q10_t a, q10_t b )
{
    return a - b ;
}

int q10_mul( q10_t a, q10_t b )
{
    return (a * b) >> Q10_SHIFT ;
}

int q10_div( q10_t a, q10_t b )
{
    return (a << Q10_SHIFT) / b ;
}

Of course you may want to be able to mix types and say multiply a q10_t by an int - providing a comprehensive library for fixed-point can get complex. Personally for that I use C++ where you have classes, function overloading and operator overloading to support more natural code. But unless your code has a great deal of general fixed point math, it may be simpler to code specific fixed point operations ad-hoc.

To take the one example you have provided:

double voltage_difference_new = ((memval3_new - memval4_new)*3.3/4096);

The floating-point there is trivially removed using millivolts:

int voltage_difference_new_mv = ((memval3_new - memval4_new) * 3300) /4096 ;

The issue then perhaps becomes one of presentation. For example if you have to present or report the value in volts to a user. In that case:

int volt_fract = abs(voltage_difference_new_mv % 1000) ;
int volt_whole = voltage_difference_new_mv / 1000 ;
printf( "%d.%04d", volt_whole, volt_fract ) ;

Upvotes: 2

Related Questions