1
\$\begingroup\$

I am helping a friend with a small electronics project using a PIC microcontroller 16F877 (A) which I am programming with mikroC.

I have run into a problem which is that I have a floating point number in a variable lets say for example 1234.123456 and need to split it out into variables holding each individual number so I get Char1 = 1, Char2=2 etc etc. for display on LCD. The number will always be rounded to 3 or 4 decimal places so there should be a need to track the location of the decimal point.

Any advice on how to get this split would be greatly appreciated.

\$\endgroup\$
3
\$\begingroup\$

There's numerous ways of doing it. You may find your compiler has a library function to do it for you. It may be possible with:

  • sprintf() / snprintf()
  • dtostrf()
  • dtoa()

Alternatively, it's not too hard to write your own routine to do it. It's just a case of first working out how many digits before the decimal point there are, dividing it by 10 that many times, then taking the integer portion repeatedly while multiplying by 10, making sure you add the decimal point in at the right place.

So in pseudo-code it may look something like:

If the value < 0.0:
    Insert - into string
    Subtract value from 0.0 to make it positive.
While the value <= 10.0:
    Divide by 10
    Increment decimal counter
For each digit of required precision:
    Take the integer portion of the value and place it in the string
    Subtract the integer portion from the value
    Decrement decimal counter
    If decimal counter is 0:
        Insert decimal point
    Multiply the value by 10.
\$\endgroup\$
2
\$\begingroup\$

Use sprintf(). It works just like printf(), but "prints" to a string (also known as an array of char).

do: char stringvar[10]; sprintf(stringvar, "%9.4f", floatvar);

\$\endgroup\$
  • 1
    \$\begingroup\$ Not all libraries have the floating point portion of vsprintf compiled in to save space. Especially on 8-bit compilers. \$\endgroup\$ – Majenko Mar 7 '15 at 0:14
  • 1
    \$\begingroup\$ @Majenko: That may be true, but if he's handling floating point numbers, there's a good chance that he's already using some floating point libraries. \$\endgroup\$ – Peter Bennett Mar 7 '15 at 0:17
  • 2
    \$\begingroup\$ The floating point mathematics libraries are completely separate from the printf floating point handling. For example, take a look at the Arduino's avr-libc. Has full floating point support, but can't printf floats because that code has been specifically excluded as it's incredibly huge. \$\endgroup\$ – Majenko Mar 7 '15 at 0:22
  • 1
    \$\begingroup\$ If he's using the floating point libraries, he probably has plenty of code space and can also include the printf library. \$\endgroup\$ – Austin Mar 7 '15 at 0:25
  • \$\begingroup\$ @Austin It's not a case of "including" the "printf library", but a question of if the built-in C library has floating point support in the vsprintf function. There's nothing you can "include" to change that, other than using a different C library. \$\endgroup\$ – Majenko Mar 7 '15 at 16:03
0
\$\begingroup\$

On a small MCU without hardware floating point support we should do as little floating point math as possible, and unless you really need the printf family of functions, try to avoid it because it bloats and slows down the code a bunch.

I suggest converting the float to an integer by first multiplying the float by 1,000.0 (assuming you want three decimal places) and then convert it to a long integer, round-off as appropriate. If you will be displaying the result on a 7-segment or dot matrix LCD then I think this format is ideal.

Let's assume that the float can be in the range 0 to 999.999 (negate if negative, save sign for display later.) The corresponding long int then has the range 0 to 999999. We will convert the number starting with the most significant digit.

Pseudo code:

dig6 = -1  // Init MSDigit
while number >= 0
   number = number - 100,000
   dig6 = dig6 + 1
number = number + 100,000  // Restore number, Dig 6 is done

dig5 = -1
while number >= 0
   number = number - 10,000
   dig5 = dig5 + 1
number = number + 10,000 // number can be switched to 16-bit here for speed

... dig4 and 3 in similar fashion

dig2 = -1
while number >= 0
   number = number - 10
   dig2 = dig2 + 1
dig1 = number + 10

At this point you have all six digits stored in a byte each and the minus sign saved. If you are using a 7-segment LCD, pass the digits to a 7-segment encoder function before writing to the LCD. If you are using dot-matrix display with serial interface, add 0x30 to each digit for ASCII encoding. We also need to remember the decimal point between dig4 and dig3.

This algorithm is quite fast since there is no multiplication and division involved. I have used it on tiny 4-bit MCUs with good results.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.