Author Topic: Arduino and decimal numbers  (Read 9741 times)

0 Members and 1 Guest are viewing this topic.

Offline Red Squirrel

  • Super Contributor
  • ***
  • Posts: 2391
  • Country: ca
Arduino and decimal numbers
« on: July 03, 2015, 07:16:36 am »
I'm working on a mini project that will require to display numerical info on a LCD (volts and amps).  To keep things clean I want the number to always take up the same amount of digits.  Going for 0.00 format where decimal point can change.   So if the value is 0.1 It will display 0.10, if it's 22 it will display 22.0 etc...   A value over 1000 would simply show 999 but it's not likely to reach that far as that would involve magic smoke assuming the readings are actually right. :P

Is there an easy way to do this, or do I have code the logic myself using string manipulation?


If it matters, I'm coding the MCU directly using Arduino software/libs and not actually using an arduino board.
 

Offline Mr.B

  • Supporter
  • ****
  • Posts: 1058
  • Country: nz
Re: Arduino and decimal numbers
« Reply #1 on: July 03, 2015, 07:21:29 am »
...or do I have code the logic myself using string manipulation?

AFAIK, yes, this is the only way to do it in Arduino.
Case statements and string manipulation.

Edit: thinking in wrong language... Switch statements...
Time is the overseer of all things.
 

Offline obiwanjacobi

  • Frequent Contributor
  • **
  • Posts: 968
  • Country: nl
  • What's this yippee-yayoh pin you talk about!?
    • Marctronix Blog
Re: Arduino and decimal numbers
« Reply #2 on: July 03, 2015, 09:16:19 am »
Perhaps you find my TextWriter class useful? It does not do as you describe but it may 'inspire' you...

http://atl.codeplex.com/SourceControl/latest#Source/Code/ArduinoTemplateLibrary/TextWriter.h

[2c]
Arduino Template Library | Zalt Z80 Computer
Wrong code should not compile!
 

Offline hamster_nz

  • Super Contributor
  • ***
  • Posts: 2197
  • Country: nz
Re: Arduino and decimal numbers
« Reply #3 on: July 03, 2015, 10:05:25 am »
You could always put it into a 999.99 format field, and then start displaying from a different place depending on if there are any leading zeros.

Code: [Select]
char *start_from;
char string[7]; // Allow an extra byte for terminating null char

// This can be done smarter, and depends on the data type for 'a'
// I'm incorrectly assuming it is a floating point number.
string[0] = (a/100)%10 + '0';
string[1] = (a/10)%10 + '0';
string[2] = (a/1)%10 + '0';
string[3] = '.';
string[4] = (a*10)%10 + '0';
string[5] = (a*100)%10 + '0';

// Work out where to display from
if(string[0] = '0')
   start_from = string+0;
else if(string[1] = '0')
   start_from = string+1;
else
   start_from = string+2;
// Add a null terminator to the string.
start_from[4] = ''\0';

output_to_the_display(start_from);
Gaze not into the abyss, lest you become recognized as an abyss domain expert, and they expect you keep gazing into the damn thing.
 

Offline f1rmb

  • Regular Contributor
  • *
  • Posts: 180
  • Country: fr
Re: Arduino and decimal numbers
« Reply #4 on: July 03, 2015, 12:06:01 pm »
Hi,

   you could use the undocumented function dtostrf() (it's part of AVR, check stdlib.h).

Cheers.
---
Daniel
 

Offline senso

  • Frequent Contributor
  • **
  • Posts: 897
  • Country: pt
    • My AVR tutorials
Re: Arduino and decimal numbers
« Reply #5 on: July 03, 2015, 12:56:03 pm »
Wont printf be able to do that with the formating options?
 

Offline MarkF

  • Super Contributor
  • ***
  • Posts: 1529
  • Country: us
Re: Arduino and decimal numbers
« Reply #6 on: July 07, 2015, 01:03:39 am »
If you're programming in C, just do a sprintf command:

Code: [Select]
float value;
char line[32];

// set value
...

// format for output (use just "%.2f" for no leading zero)
sprintf(line,"%0.2f",value);

// send line to the display
...
 

Offline fake-name

  • Regular Contributor
  • *
  • Posts: 74
Re: Arduino and decimal numbers
« Reply #7 on: July 07, 2015, 01:26:41 am »

   you could use the undocumented function dtostrf() (it's part of AVR, check stdlib.h).


What part of it's undocumented? It's not documented in the arduweenie stuff, but their documentation is garbage anyways.
 

Offline miguelvp

  • Super Contributor
  • ***
  • Posts: 5549
  • Country: us
Re: Arduino and decimal numbers
« Reply #8 on: July 07, 2015, 01:30:35 am »
Regarding printf and sprintf.

I guess if you can take the about 2KB overhead of the function it would be a solution, but not everyone wants to waste that much space for just printing some characters.

Maybe you have space now, but what happens when your program needs to do more?
 

Offline MarkF

  • Super Contributor
  • ***
  • Posts: 1529
  • Country: us
Re: Arduino and decimal numbers
« Reply #9 on: July 07, 2015, 01:49:54 am »
Regarding printf and sprintf.

I guess if you can take the about 2KB overhead of the function it would be a solution, but not everyone wants to waste that much space for just printing some characters.

Maybe you have space now, but what happens when your program needs to do more?

If you want to do everything in integers, keep all your values * 100.  Then when you want to display them, divide by 100 to get the integer part and do a modulo 100 to get the decimal part.
 

Offline hamster_nz

  • Super Contributor
  • ***
  • Posts: 2197
  • Country: nz
Re: Arduino and decimal numbers
« Reply #10 on: July 07, 2015, 02:05:32 am »
If you're programming in C, just do a sprintf command:

test1() performs the conversion, test2() uses sprintf():

Code: [Select]
#include <stdio.h>

void test1(float value) {
  char *start_from;
  char string[7]; // Allow an extra byte for terminating null char
  unsigned a;

  // 'a' is 100*value, but restricted in range 
  if(value > 999.0) a = 99999;
  else if(value < 0) a = 0;
  else a = value * 100;

  // Extract the digits
  string[6] = a%10 + '0'; a /= 10;
  string[5] = a%10 + '0'; a /= 10;
  string[4] = '.';
  string[3] = a%10 + '0'; a /= 10;
  string[2] = a%10 + '0'; a /= 10;
  string[1] = a%10 + '0';
  string[0] = ' ';

  // Work out where to display from
  start_from = string;
  if(string[1] != '0')
    start_from += 0;  // Leading space, 3 digits
  else if(string[2] != '0')
    start_from += 2;  // Two digits, one decimal
  else
    start_from += 3;  // One digit, two decimals

  // Add a terminator
  start_from[4] = '\0';

  // Display it
  printf("Test1 is '%s'\r\n",start_from);
}

void test2(float a)
{
  char line[32];
  sprintf(line,"%0.2f",a);
  printf("Test2 is '%s'\r\n",line);
}

int main(int argc, char *argv[])
{
  float value = 0.1;

  printf("Input is %f\r\n",value);
  test1(value);
  test2(value);
  puts("\r\n");

  value = 22.0;
  printf("Input is %f\r\n",value);
  test1(value);
  test2(value);
  puts("\r\n");

  value = 100.0;
  printf("Input is %f\r\n",value);
  test1(value);
  test2(value);
  puts("\r\n");

  value = 123456789.0;
  printf("Input is %f\r\n",value);
  test1(value);
  test2(value);
  puts("\r\n");

  value = -1.0;
  printf("Input is %f\r\n",value);
  test1(value);
  test2(value);

  puts("\r\n");
}

And the output:

Code: [Select]
Input is 0.100000
Test1 is '0.10'
Test2 is '0.10'


Input is 22.000000
Test1 is '22.0'
Test2 is '22.00'


Input is 100.000000
Test1 is ' 100'
Test2 is '100.00'


Input is 123456792.000000
Test1 is ' 999'
Test2 is '123456792.00'


Input is -1.000000
Test1 is '0.00'
Test2 is '-1.00'



Also, using sprintf() just once will increase your program's code size quite a bit, but if you using it elsewhere then why not...
Gaze not into the abyss, lest you become recognized as an abyss domain expert, and they expect you keep gazing into the damn thing.
 

Offline miguelvp

  • Super Contributor
  • ***
  • Posts: 5549
  • Country: us
Re: Arduino and decimal numbers
« Reply #11 on: July 07, 2015, 02:06:27 am »
Regarding printf and sprintf.

I guess if you can take the about 2KB overhead of the function it would be a solution, but not everyone wants to waste that much space for just printing some characters.

Maybe you have space now, but what happens when your program needs to do more?

If you want to do everything in integers, keep all your values * 100.  Then when you want to display them, divide by 100 to get the integer part and do a modulo 100 to get the decimal part.

I wasn't talking about support for %f but just using sprintf even without float support will add around 2KB to your program. Maybe that's not an issue but it could be one.

And if you just want to print some numbers, why do you want to carry the sprintf extra baggage?
 

Offline f1rmb

  • Regular Contributor
  • *
  • Posts: 180
  • Country: fr
Re: Arduino and decimal numbers
« Reply #12 on: July 07, 2015, 04:04:44 am »

   you could use the undocumented function dtostrf() (it's part of AVR, check stdlib.h).


What part of it's undocumented? It's not documented in the arduweenie stuff, but their documentation is garbage anyways.

What part ? Seriously ?

daniel@daniel-ThinkPad-X201 ~ $ man dtostrf
No manual entry for dtostrf


Since when "dtostrf" or "dtostre" are C standard (stdlib) . It's an AVRism, and if you don't jump reading stdlib.h to see the function prototype, no way you miraculously think of it.

BTW, nice and useful comment  :palm:

To the OP, I share the same advice, try to avoid sprintf() if possible, it's a memory hog.


Cheers.
---
Daniel
« Last Edit: July 07, 2015, 04:06:55 am by f1rmb »
 

Offline miguelvp

  • Super Contributor
  • ***
  • Posts: 5549
  • Country: us
Re: Arduino and decimal numbers
« Reply #13 on: July 07, 2015, 04:19:42 am »
Don't look, kind of looks like documentation to me:

http://www.atmel.com/webdoc/AVRLibcReferenceManual/group__avr__stdlib_1ga060c998e77fb5fc0d3168b3ce8771d42.html

Just because it's not on your man pages doesn't mean it's undocumented :)
 

Offline f1rmb

  • Regular Contributor
  • *
  • Posts: 180
  • Country: fr
Re: Arduino and decimal numbers
« Reply #14 on: July 07, 2015, 04:33:51 am »
Don't look, kind of looks like documentation to me:

http://www.atmel.com/webdoc/AVRLibcReferenceManual/group__avr__stdlib_1ga060c998e77fb5fc0d3168b3ce8771d42.html

Just because it's not on your man pages doesn't mean it's undocumented :)

Once again, it's not a C standard function but an AVRism, unlike *printf(), or did I missed something for the last 25 years ?  ;D
If it's not in man pages, it doesn't even exists  ;)

Cheers.
---
Daniel
 

Offline miguelvp

  • Super Contributor
  • ***
  • Posts: 5549
  • Country: us
Re: Arduino and decimal numbers
« Reply #15 on: July 07, 2015, 04:48:24 am »
hmm, undocumented to me means there is no documentation available, and nothing to do with being part of the C standard libraries.

I always thought that even standard C libraries are not really part of C, since they are just libraries, but hey, when in Rome!
 

Offline SL4P

  • Super Contributor
  • ***
  • Posts: 2117
  • Country: au
  • There's more value if you figure it out yourself!
Re: Arduino and decimal numbers
« Reply #16 on: July 07, 2015, 05:14:34 am »
For all the hate levelled at printf() and it's derivatives - I love it, as it let's me get the output results I want i a convenient and standardized manner.  decimals, justification, fill - everything you want today or have wanted for the last 30 years!

Yes it occupies memory, and if you want to do just one specific format - write your own output function - otherwise stop complaining!  :blah:
Don't ask a question if you aren't willing to listen to the answer.
 

Offline Red Squirrel

  • Super Contributor
  • ***
  • Posts: 2391
  • Country: ca
Re: Arduino and decimal numbers
« Reply #17 on: July 07, 2015, 10:02:11 am »
Thanks for the ideas, I'll play around with those.

Goal is to probably use ints throughout most of the program, given the ADC produces a value from 1 to 1024 it's probably easier for me to times it by a certain value so I get a number in the 10,000's then just add a decimal point at the right spot. I read somewhere that division is slower on MCUs so to try to avoid it.  Probably not a big issue for this particular app but may as well try to use good habits.  I'll try to avoid springf as well.

Come to think of it all I really need to do here is get the value converted to a string, then I can do a char by char analsys to decide what digits to display and where to put to decimal.
 

Offline SL4P

  • Super Contributor
  • ***
  • Posts: 2117
  • Country: au
  • There's more value if you figure it out yourself!
Re: Arduino and decimal numbers
« Reply #18 on: July 07, 2015, 10:21:39 am »
I read somewhere that division is slower on MCUs so to try to avoid it.
Keep in mind that bit shifting / rotating is very fast, and you can do quite a lot of maths with combinations of the above... also as you said - think (integer x 1xxx), then plonk a decimal back where it belongs!
Don't ask a question if you aren't willing to listen to the answer.
 

Offline MarkF

  • Super Contributor
  • ***
  • Posts: 1529
  • Country: us
Re: Arduino and decimal numbers
« Reply #19 on: July 07, 2015, 11:22:22 am »
I read somewhere that division is slower on MCUs so to try to avoid it.
Keep in mind that bit shifting / rotating is very fast, and you can do quite a lot of maths with combinations of the above... also as you said - think (integer x 1xxx), then plonk a decimal back where it belongs!

Most  modern compilers know this too.  Don't try to out smart them.  Write your code the most straight forward way and then look at the generated assembly if you need to try to optimize it yourself.
 

Offline westfw

  • Super Contributor
  • ***
  • Posts: 3125
  • Country: us
Re: Arduino and decimal numbers
« Reply #20 on: July 08, 2015, 07:34:50 am »
Quote
Test2 is '22.00'
I'll point out that this is NOT the desired output.  I haven't done a lot with floating point printf, but I couldn't immediately come up with a printf format that WOULD produce the correct results.  (Nor a fortran format, either :-))
Now that several people have suggested a format that doesn't work, I guess I'll ask explicitly:  IS there a format that will produce those outputs?  IIRC, the OP wanted:
0.01
0.22
3.33
44.4
 555
6666
9999
 

Offline MarkF

  • Super Contributor
  • ***
  • Posts: 1529
  • Country: us
Re: Arduino and decimal numbers
« Reply #21 on: July 08, 2015, 08:02:08 am »
Quote
Test2 is '22.00'
I'll point out that this is NOT the desired output.  I haven't done a lot with floating point printf, but I couldn't immediately come up with a printf format that WOULD produce the correct results.  (Nor a fortran format, either :-))
Now that several people have suggested a format that doesn't work, I guess I'll ask explicitly:  IS there a format that will produce those outputs?  IIRC, the OP wanted:
0.01
0.22
3.33
44.4
 555
6666
9999

Short answer:  NO.
Long answer:  He would need to check the magnitude of the variable before doing the format.

Code: [Select]
float val;
char txt[32];

if (val < 10.0) {
   sprintf(txt,"%4.2f",val);   // format with 2 decimal digits
}
else if (val < 100.0) {
   sprintf(txt,"%4.1f",val);   // format with 1 decimal digit
}
else if (val < 1000.0 {
   sprintf(txt,"%4.0f",val);   // format with 0 decimal digits
}
else {
   sprintf(txt," 999");   // display 999 if greater than or equal to 1000
}
 

Offline hamster_nz

  • Super Contributor
  • ***
  • Posts: 2197
  • Country: nz
Re: Arduino and decimal numbers
« Reply #22 on: July 08, 2015, 10:11:42 am »
Code: [Select]
float val;
char txt[32];

if (val < 10.0) {
   sprintf(txt,"%4.2f",val);   // format with 2 decimal digits
}
else if (val < 100.0) {
   sprintf(txt,"%4.1f",val);   // format with 1 decimal digit
}
else if (val < 1000.0 {
   sprintf(txt,"%4.0f",val);   // format with 0 decimal digits
}
else {
   sprintf(txt," 999");   // display 999 if greater than or equal to 1000
}

Annoying corner case time :) ... 99.99 is less than 100.0 ,but will produce a 5 character output with a "%4.1f" format due to rounding. Grrr..

Code: [Select]
$ cat a.c
#include <stdio.h>

int main(int argc, char *argv)
{
   printf("%4.1f\r\n",(float)99.5);
   printf("%4.1f\r\n",(float)99.99);
}
$ gcc -o a a.c ; ./a
$ ./a
99.5
100.0

so you also need to use limits that allow for the effects of rounding.

Also, with an embedded display when you jump form "9.99" to "10.0" and back again rapidly the display can become hard to read. This can
be really non-trivial to solve.
Gaze not into the abyss, lest you become recognized as an abyss domain expert, and they expect you keep gazing into the damn thing.
 

Offline MarkF

  • Super Contributor
  • ***
  • Posts: 1529
  • Country: us
Re: Arduino and decimal numbers
« Reply #23 on: July 08, 2015, 05:23:39 pm »
I guess the easiest way would be to truncate the value before formatting it:

Code: [Select]
void toAscii(int x, int y, float val)
{
   char txt[16];

   if (val < 10.)
      val = floor(val*100.)/100.;
   else if (val < 100.)
      val = floor(val*10.)/10.;
   else
      val = floor(val);

   if (val < 10.)
      sprintf(txt,"%4.2f",val);
   else if (val < 100.)
      sprintf(txt,"%4.1f",val);
   else if (val < 1000.)
      sprintf(txt,"%4.0f",val);
   else
      sprintf(txt," 999");

   drawTextToDisplay(x,y,txt);
}
 

Offline NANDBlog

  • Super Contributor
  • ***
  • Posts: 4563
  • Country: nl
  • Current job: ATEX certified product design
Re: Arduino and decimal numbers
« Reply #24 on: July 08, 2015, 05:32:46 pm »
Regarding printf and sprintf.

I guess if you can take the about 2KB overhead of the function it would be a solution, but not everyone wants to waste that much space for just printing some characters.

Maybe you have space now, but what happens when your program needs to do more?

If you want to do everything in integers, keep all your values * 100.  Then when you want to display them, divide by 100 to get the integer part and do a modulo 100 to get the decimal part.

I wasn't talking about support for %f but just using sprintf even without float support will add around 2KB to your program. Maybe that's not an issue but it could be one.

And if you just want to print some numbers, why do you want to carry the sprintf extra baggage?
Most code for Arduino will contain printf for debugging reason anyway, so I'm not sure why would be a problem to use it. The whole reason for using a Arduino is to reduce the time required to connect the stuff and write the code, when efficiency is irrelevant.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf