Go Down

### Topic: printing a double variable (Read 87494 times)previous topic - next topic

#### mem

#15
##### Nov 17, 2008, 09:48 pmLast Edit: Nov 18, 2008, 10:40 am by mem Reason: 1
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: [Select]
`#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 valuedouble d = 0.123456; // a double test valuevoid 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

#### Shepparton

#16
##### Nov 18, 2008, 07:45 am
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.

Code: [Select]
`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

#### Shepparton

#17
##### Nov 18, 2008, 08:10 am
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

#### mem

#18
##### Nov 18, 2008, 10:45 amLast Edit: Nov 18, 2008, 11:27 am by mem Reason: 1
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.

#### Shepparton

#19
##### Nov 19, 2008, 05:55 am
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

#### mem

#20
##### Nov 19, 2008, 06:56 am
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.

#### tony

#21
##### Jan 22, 2009, 07:53 pm
Mem. You are the man. Thank you this is exactly what I needed, you just saved me a boatload of time!

#### guiambros

#22
##### Jan 24, 2009, 09:06 am
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?

#### mem

#23
##### Jan 24, 2009, 10:06 amLast Edit: Jan 24, 2009, 10:08 am by mem Reason: 1
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!

#### mikalhart

#24
##### Jan 24, 2009, 07:30 pm
Mem is quite right.  Here's an instructive experiment:

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

Mikal

#### kg4wsv

#25
##### Jan 24, 2009, 08:13 pm
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

#### mikalhart

#26
##### Jan 24, 2009, 09:26 pm
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

#### halley

#27
##### Jan 25, 2009, 05:39 amLast Edit: Jan 25, 2009, 05:51 am by halley Reason: 1
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 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.

#### guiambros

#28
##### Jan 26, 2009, 05:24 am
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.

#### flyboy

#29
##### Jan 29, 2009, 05:09 pm
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!

Go Up