Go Down

Topic: printing double-precision floating point (Read 1 time) previous topic - next topic

John Beale

Mar 13, 2013, 02:24 am Last Edit: Mar 13, 2013, 08:51 pm by John Beale Reason: 1
In case it is useful, here is an updated version of an older Arduino sketch to convert floating point numbers to a string in scientific notation for printing.
This one shows up to 16 decimal digits, so you can see the full accuracy of your double-precision float types. (Note, I have only tested it on a Teensy 3, not a Due.)
This fits into the category of "quick hack", it is not in any way optimized.

Sample output string:  3.1415926535897926 E+146

Try running this sketch on an older ATmega type Arduino, and you will see quite a difference in the range and precision of floating point values (because the original Arduinos set type double == type float).

Code: [Select]
//
// Test of function that builds a C-style "string" for a floating point
// number in scientific notation.
//
// original by davekw7x December, 2010  
// http://arduino.cc/forum/index.php/topic,46931.0.html
// modified to use "double" and to run to smaller / larger values  March 12 2013 JPB
//

#define STEPS 330

void setup()
{
   double f, fa;
   int d;
   char str[26];  // formated number as string
   
   Serial.begin(115200);
   delay(3000);
   Serial.println("double precision floating point test");

   f = 0.0;
   // No "digits" argument means 2 digits
   Serial.print("0.0 = ");Serial.println(double2s(f));Serial.println();
   
   // Rounding with leading zeros in the fractional part
   f = 10.3456;
   Serial.print(f,4);Serial.print(" = ");Serial.println(double2s(f, 4));Serial.println();
   
   /*
      For loops 1 and 2, note that floating point
      overflows result values of"INF"
   */
   f = PI;

   
   Serial.println("Loop 1");
   for (int i=1; i< STEPS; i++)
   {
       Serial.println(double2s(f, 16));
       if (isinf(f)) break;        
       f *=1E1;
   }
   Serial.println();

   f = -PI;
   Serial.println("Loop 2");
   for (int i=1; i< STEPS; i++)
   {
       Serial.println(double2s(f, 16));
       if (isinf(f)) break;        
       f *=1E1;
   }  
   Serial.println();

   /*
      For loops 3 and 4 note that floating point
      underflows result in values that go to zero.
   */
   f = PI;
   Serial.println("Loop 3");
   for (int i=1; i< STEPS; i++)
   {
       Serial.println(double2s(f, 16));        
       if (f == 0.0) break;
       f /= 1E1;
   }
   Serial.println();

   f = -PI;
   Serial.println("Loop 4");
   for (int i=1; i< STEPS; i++)
   {
       Serial.println(double2s(f, 16));
       if (f == 0.0) break;
       f /= 1E1;
   }
   Serial.println();

   /*
      Loop 5 shows rounding as follows:
       6: 1.999518 E+20
       5: 1.99952 E+20
       4: 1.9995 E+20
       3: 2.000 E+20
       2: 2.00 E+20
       1: 2.0 E+20
       0: 2 E+20
   */

   f = 1.999518e20;
   Serial.println("Loop 5");
   for (int i = 16; i >= 0; i--) {
       Serial.print(i);Serial.print(": ");Serial.println(double2s(f, i));
   }
   Serial.println();
   
   /*
      Loop 6 shows rounding as follows:
       6: 1.999496 E+20
       5: 1.99950 E+20
       4: 1.9995 E+20
       3: 1.999 E+20
       2: 2.00 E+20
       1: 2.0 E+20
       0: 2 E+2
   */
   
   f = 1.999496e20;
   Serial.println("Loop 6");
   for (int i = 16; i >= 0; i--) {
       Serial.print(i);Serial.print(": ");Serial.println(double2s(f, i));
   }    
   Serial.println();
   
   Serial.println("NaN tests");
   f = sqrt(-1);
   Serial.print("sqrt(-1) = ");Serial.println(double2s(f, 3));

   f = 0.0/0.0;
   Serial.print("0.0/0.0 = ");Serial.println(double2s(f, 4));

   f = INFINITY * 0;
   Serial.print("INFINITY*0 = ");Serial.println(double2s(f, 5));Serial.println();

   
   Serial.println("INFINITY tests");
   f = 1.0/0;
   Serial.print("1.0/0 = ");Serial.println(double2s(f, 1));
   //printBytes(f);

   f = -1.0/0;
   Serial.print("1.0/-0 = ");Serial.println(double2s(f, 1));
   //printBytes(f);

   f = -1.0/0L;
   Serial.print("1.0/0L = ");Serial.println(double2s(f, 2));
   
   f = 1.0/0UL;
   Serial.print("1.0/0UL = ");Serial.println(double2s(f, 3));
   
   Serial.println();
   
   // Note that tan(pi/2) may not result in INF due
   // to limited precision.
   //
   f = tan(HALF_PI);
   Serial.print("tan(");Serial.print(HALF_PI, 6);Serial.print(") = ");  
   Serial.println(double2s(f, 6));
}

void loop()
{
}


// ====== double2s(): print out up to 16 digits of input double-precision value
// This version enables double-precision for Teensy 3, etc.
// by J.Beale March 2013
// modified from original float2s() posted by davekw7x on December, 2010
// http://arduino.cc/forum/index.php/topic,46931.0.html

char * double2s(double f)
{
 return double2s(f, 2);
}

char * double2s(double f, unsigned int digits) {    
int d;
static char s[26];  // formated number as string
int index=0;

 // handle sign
 if (f < 0.0)
 {
   s[index++] = '-';
   f = -f;
 }
 // handle infinite values
 if (isinf(f))
 {
   strcpy(&s[index], "INF");
   return s;
 }
 // handle Not a Number
 if (isnan(f))
 {
   strcpy(&s[index], "NaN");
   return s;
 }

 // max digits
 if (digits > 16) digits = 16;
 int exponent = int(log10(f));
 double g = f / pow(10, exponent);
 if ((g < 1.0) && (g != 0.0))      
 {
   g *= 10;
   exponent--;
 }
 if (exponent < -330) {  // lower limit of double-precision on Teensy 3
   g = 0;
   exponent = 0;
 }
 if (digits < 16) {  // display number rounded at last digit
   g += 0.5 / pow(10,digits);
 }
 
 d = g;  
 sprintf(&s[index++],"%d",d);  
 if (digits > 0) sprintf(&s[index++],".");
 for (int i=0;i<digits;i++) {
     g = (g - d) * 10.0;  // shift one decimal place to the left
     d = int(g);
     sprintf(&s[index++],"%d",d);
 }
 sprintf(&s[index], " E%+d", exponent);
 return s;    
} // ===== end double2s()


Go Up