3

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.

HFOrangefish
  • 267
  • 1
  • 10
  • 3
    Do the units have to be metres? Using (integer) millimetres will give you 1,000 times better resolution. – Adrian Mole Apr 03 '22 at 12:19
  • The units are in centimeters for the range, but you're right. mms would be better. – HFOrangefish Apr 03 '22 at 12:23
  • Depending on the maximum range, you could even use micrometres. For a 32-bit integer, you would still have a max range of over 2 km. – Adrian Mole Apr 03 '22 at 12:26
  • 5
    Use [fixed-point arithmetic](https://en.wikipedia.org/wiki/Fixed-point_arithmetic)? – pmg Apr 03 '22 at 12:27
  • 1
    Even if your CPU doesn't have hardware floating point support, you can still use `float` and `double`. It's then implemented in software. Is there a specific need to avoid them? – Codo Apr 03 '22 at 12:29
  • 1
    @Codo Due to hardware limitations, the speed at which floats are processed is so frustratingly terrible that I wouldn't be able to have any good sampling frequency. – HFOrangefish Apr 03 '22 at 12:33
  • So you actually have a performance issue, and you have carefully analyzed it and found that the software floating point operations take most of the execution time. – Codo Apr 03 '22 at 12:40
  • Yes, the floating point operations take too much time, and a lot more than the int operations. Lack of hardware floating point support. – HFOrangefish Apr 03 '22 at 12:48
  • 1
    @HFOrangefish I think [this question](https://stackoverflow.com/questions/10067510/fixed-point-arithmetic-in-c-programming) can help you. – Zakk Apr 03 '22 at 13:07
  • There are infinitely many real numbers. There are only finitely many states possible in a practical computer program. Therefore, you must choose a subset of the real numbers to work with. Edit the question to state what set of fractions you need or are willing to accept. For example, if the denominator will always be 4096 or a factor of it, state that, and state the limits on the numerator. – Eric Postpischil Apr 03 '22 at 13:14

1 Answers1

2

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 ) ;
Clifford
  • 88,407
  • 13
  • 85
  • 165