Pages: 1 [2] 3   Go Down
Author Topic: printing a double variable  (Read 29258 times)
0 Members and 1 Guest are viewing this topic.
London
Offline Offline
Faraday Member
**
Karma: 8
Posts: 6240
Have fun!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

double and float are the same in the arduino environment. The routine works fine for me, here is the test sketch I used with the 0012 liquidcrystal library:
Code:
#include <LiquidCrystal.h>

// LiquidCrystal display with:
// rs on pin 7        (LCD pin 4 ) aka DI
// rw on pin 6        (LCD pin 5)
// enable on pin 5 (LCD pin 6)
// d4, d5, d6, d7 on pins 9, 10, 11, 12  (LCD pins 11-14)

LiquidCrystal lcd(7, 6, 5, 9, 10, 11, 12);

void setup()
{
  // Print a message to the LCD.
  lcd.print("float test!");
  delay(2000);
  lcd.clear();  
}

float f = 3.1415; // a floating point test value
double d = 0.123456; // a double test value

void loop()
{

   lcd.setCursor(0, 0) ;
   lcdPrintFloat(f,4  ); // show value of f with 4 digits after decimal point
   lcd.setCursor(0,1) ;
   lcdPrintFloat(d,6);   // show value of d with 6 digits after decimal point
   delay(2000);
   f -= 1.0;  // subtract 1 from the value to print on the next loop
   d +=.123456;   // add 0.123456 to the value and print on the next loop
}

void lcdPrintFloat( float val, byte precision){
  // prints val on a ver 0012 text lcd with number of decimal places determine by precision
  // precision is a number from 0 to 6 indicating the desired decimial places
  // example: lcdPrintFloat( 3.1415, 2); // prints 3.14 (two decimal places)

  if(val < 0.0){
    lcd.print('-');
    val = -val;
  }

  lcd.print ((long)val);  //prints the integral part
  if( precision > 0) {
    lcd.print("."); // print the decimal point
    unsigned long frac;
    unsigned long mult = 1;
    byte padding = precision -1;
    while(precision--)
  mult *=10;

    if(val >= 0)
 frac = (val - int(val)) * mult;
    else
 frac = (int(val)- val ) * mult;
    unsigned long frac1 = frac;
    while( frac1 /= 10 )
 padding--;
    while(  padding--)
 lcd.print("0");
    lcd.print(frac,DEC) ;
  }
}

edit: code updated, function renamed

Note that my LCD panel is wired differently from the Arduino example sketch. Make sure your sketch pins match the wiring you have used to connect the panel

if you use the old lcd4bit library you will need to modify the syntax. it may be easier switching to the new arduino library
« Last Edit: November 18, 2008, 04:40:48 am by mem » Logged

Shepparton Vic
Offline Offline
Newbie
*
Karma: 0
Posts: 10
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi
this is the code I use to print a double to either LCD or monitor.(I can't take credit for it, i just modified it to fit my needs) If you search arduino home tempToAscii you will find the original author. Sorry I don't remember who. But thanks a lot.

The good thing about this function is that it returns an ascii string.
Code:
char* tempToAscii(float temp)
// convert long to type char and store in variable array ascii
{
  char ascii[20];// needs to be this big to hold a type float

  int frac;
  int rnd;

    rnd = (unsigned int)(temp*1000)%10;
    frac=(unsigned int)(temp*100)%100;  //get three numbers to the right of the deciaml point.
    if (rnd>=5) frac=frac+1;
    itoa((int)temp,ascii,10);           // I changed it to 2 decimal places
    strcat(ascii,".");

  if (frac<10)  {itoa(0,&ascii[strlen(ascii)],10); }   // if fract < 10 should print .0 fract ie if fract=6 print .06

    itoa(frac,&ascii[strlen(ascii)],10); //put the frac after the deciaml
    
  return ascii;
}
Further code to show how I use it

 lcd.print(tempToAscii(tempC))

or
Logged

Shepparton Vic
Offline Offline
Newbie
*
Karma: 0
Posts: 10
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

sorry about double posting I pressed the wrong button before I had finished. :-[

I use this code to display temperature readings from two DS18B20 sensors on a LCD. That's why it is only to 2 decimal places.
It could easily be modified to however many places you want.
What I like about this function is that you only have characters to play with and they are the easiest to print

cheers
Logged

London
Offline Offline
Faraday Member
**
Karma: 8
Posts: 6240
Have fun!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi Shepparton, there are a few disadvantages in using the code in reply#16. It requires some RAM  permanently reserved for the conversion routine (BTW, there is a bug in the code, char ascii[20] needs to be static otherwise the value may not be preserved when the calling function accesses the return value). It also hard codes the number of decimal places.

For applications that need to have the floating point value returned as a string, I think the code posted by Don Kinzer in reply #11 in this thread would be a better choice.

But for displaying floating point on an LCD or serial terminal, the code posted in reply#13 makes the most efficient use RAM.

I have decided to rename the function I posted to lcdPrintFloat and have edited the code in reply#15 accordingly. It will still work with double values.  This thread started with a question about how to display a double value on the serial terminal but I think most people looking for a solution are thinking about float, so I hope the rename will be helpful.
« Last Edit: November 18, 2008, 05:27:17 am by mem » Logged

Shepparton Vic
Offline Offline
Newbie
*
Karma: 0
Posts: 10
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks Mem.
I'm still new to C and micros with limited memory and how it is used so any advice is gratefully accepted.
Just one question (although this may not be the correct thread) would it be possible to have a repository for all useful functions in one place? Perhaps in the playground where members could be invited to post their code?
I know that for someone like me who is still learning it would be of immense value.
cheers
Logged

London
Offline Offline
Faraday Member
**
Karma: 8
Posts: 6240
Have fun!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

The playground is the repository for user contributed code and techniques and the routines for displaying gloating point really should be there.  I guess everyone is too busy tinkering to do the write-ups on stuff like this.  
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 8
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Mem. You are the man. Thank you this is exactly what I needed, you just saved me a boatload of time!
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 5
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

hello folks,

I'm trying the PrintDouble function above, but doesn't work for me. Try with these numbers:

  printDouble(32766.6613, 2);
  // result = 32766.66   >> OK!

  printDouble(32766.6613, 4);
  // result = 32766.6621  >> WRONG!

  printDouble(1.6613, 4);
  // result = 1.6612        >> CLOSE, BUT WRONG!

  printDouble(-32766.1234, 2);
  // result = -32766.12        >> OK!

  printDouble(-32766.1234, 4);
  // result = -32766.1230 >> CLOSE, BUT WRONG!


Any ideas what may be happening?  What is funny is that I did my own routine (but using similar technique, just using float) and found exactly the same numbers. Seems to be some bug or limitation on at168/arduino's math implementation.

any ideas what's the cause and how to solve?
Logged

London
Offline Offline
Faraday Member
**
Karma: 8
Posts: 6240
Have fun!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

The arduino uses 4 bytes for both float and double values.  This has limited accuracy when expressing large numbers with lots of decimal places and this is what is causing the errors.

does it matter if  -32766.1234 is expressed as -32766.1230  ?

Are you really doing something that justifies an accuracy better than  0.000001 %

If you are, you may be better off converting your floating point to long integers by multiplying your values by 10000 and doing all your math as long integers. You can print the result as if it was a float by printing a decimal point in the appropriate place. But be careful you don't overflow the range of values for a long integer!
« Last Edit: January 24, 2009, 04:08:39 am by mem » Logged

Austin, TX USA
Offline Offline
God Member
*****
Karma: 4
Posts: 997
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Mem is quite right.  Here's an instructive experiment:

Code:
void setup()
{
  Serial.begin(9600);
  if (-32766.1234 == -32766.1230)
    Serial.println("Wow, they're equal! :)");
}

void loop(){}

Mikal
Logged

0
Offline Offline
Faraday Member
**
Karma: 7
Posts: 2526
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

It is important to note that floating point representation is an approximation, not an exact value.

You should get consistent results between runs (assuming identical calculations in identical order), and if the platform is adhering to the IEEE standard for floating point representation it will even be consistent across platforms, but the fat remains it's an approximation.

-j

Logged

Austin, TX USA
Offline Offline
God Member
*****
Karma: 4
Posts: 997
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
It is important to note that floating point representation is an approximation, not an exact value.

That's exactly right.  I failed to make the point that printDouble() is simply reporting, and not causing the apparent inaccuracy.

M
Logged

Connecticut, US
Offline Offline
Edison Member
*
Karma: 2
Posts: 1036
Whatduino
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

For a bit more understanding of floating point, it helps to know how this is usually implemented at the binary level.

In decimal,  1000 == 103 and 0.001 = 10-3.  You have "significant digits" and you have an "exponent."

In binary,  B1000 == 23 (decimal smiley-cool and B0.001 == 2-3 (decimal 0.125).  Note that B0.1 is one half, B0.01 is one fourth, and B0.001 is one eighth.

As you see, the number of digits it takes to write a given value can be different if you store it in binary or decimal.  One sixteenth is just a 1 shifted over a few places in binary, but it's 0.0625 in decimal, three digits.  Likewise, it's easy to say "one tenth" in decimal, but in binary, it takes infinite precision to write it perfectly:  B0.00011001100110011001100110011... which is why you start seeing roundoff errors when doing fine fiddly math on computers, even with statements like z += 0.1;.

You can store the significant bits and the exponent as two signed integers crammed into the same 32-bit space, but you need to know how many bits are for the significant digits, and how many bits are for the exponent.  As the exponent gets bigger and bigger, you need to allocate more bits for holding it, at the expense of significant digit precision.  It is this "tradeoff" between the two, and the way the precision can "float" so as to give the best precision for any given exponent, that gives the "floating point" technique its name.

More can be learned if you look at the most common implementation, IEEE 754 standard.  http://steve.hollasch.net/cgindex/coding/ieeefloat.html  -- the only gotcha here is that Arduino's double and float types are both the same thing, really:  the 32-bit "single precision" format.
« Last Edit: January 24, 2009, 11:51:14 pm by halley » Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 5
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

great explanation Halley!  Really useful stuff.

Thanks also Mikal. Your example is quite useful to demonstrate printDouble (and other similar clones) are not introducing the error, but simply reporting what's really stored on the variable.


Logged

Central Indiana, USA
Offline Offline
Full Member
***
Karma: 0
Posts: 202
So many projects, so little time...and money!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I've been working with this for several days now.  Something of interest to everyone trying to do this...if you use lcd.print instead of lcdPrintDouble, you will get a binary result on your LCD.  :-?
It was a very simple mistake, but proved to be very frustrating as well.
Have fun!
Logged

Pages: 1 [2] 3   Go Up
Jump to: