el_supremo
Well-known member
[edit]This is on Teensy 3/3.1[/edit]
About a year ago I wrote a program to parse algebraic equations and using that I wrote code to do numerical integration. I haven't used it for quite a while but today I found that it is giving me incorrect results whereas previously it was working fine. I eventually tracked the problem down to the way that double precision constants are stored using Arduino 1.6.5_r2 (with TeensyDuino 1.25 or 1.26).
A constant such as M_PI, which is defined in math.h as 3.14159265358979323846264338, should (by default) be compiled as double precision but in Arduino version 1.6.5_r2 it is obviously storing it as a float because it is printed as 3.1415927410125732 which is only correct to 7 places.
Here's some code which shows the problem using a function I modified to print double precision and using the floating point version of printf.
This code runs correctly with Arduino versions 1.0.6, 1.6.1 and 1.6.4
Anyone run into this?
Am I forgetting a magic incantation or is something up the creek?
Pete
About a year ago I wrote a program to parse algebraic equations and using that I wrote code to do numerical integration. I haven't used it for quite a while but today I found that it is giving me incorrect results whereas previously it was working fine. I eventually tracked the problem down to the way that double precision constants are stored using Arduino 1.6.5_r2 (with TeensyDuino 1.25 or 1.26).
A constant such as M_PI, which is defined in math.h as 3.14159265358979323846264338, should (by default) be compiled as double precision but in Arduino version 1.6.5_r2 it is obviously storing it as a float because it is printed as 3.1415927410125732 which is only correct to 7 places.
Here's some code which shows the problem using a function I modified to print double precision and using the floating point version of printf.
Code:
#include <math.h>
void test_printDoubleln(double number, uint8_t digits)
{
test_printDouble(number,digits);
Serial.println("");
}
void test_printDouble(double number, uint8_t digits)
{
uint8_t sign=0;
// Handle negative numbers
if (number < 0.0) {
sign = 1;
number = -number;
}
// Round correctly so that print(1.999, 2) prints as "2.00"
double rounding = 0.5;
for (uint8_t i=0; i<digits; ++i) {
rounding *= 0.1;
}
number += rounding;
// Extract the integer part of the number and print it
unsigned long int_part = (unsigned long)number;
double remainder = number - (double)int_part;
if(sign)Serial.print("-");
Serial.print(int_part);
// Print the decimal point, but only if there are still
// fractional digits to be printed
if (digits > 0) {
//>>> buf[8] is inadequate - use buf[20]
uint8_t n, buf[20], count=1;
buf[0] = '.';
// Extract digits from the remainder one at a time
if (digits > sizeof(buf) - 1) digits = sizeof(buf) - 1;
while (digits-- > 0) {
remainder *= 10.0;
n = (uint8_t)(remainder);
if(n > 9) {
buf[count++] = '?';
break;
}
buf[count++] = '0' + n;
remainder -= n;
}
Serial.write(buf,count);
}
}
void setup()
{
asm(".global _printf_float");
Serial.begin(9600);
while (!Serial);
delay(100);
Serial.println("Should be\n3.14159265358979323846264338");
// This is the big problem. This is rounded to a float
test_printDoubleln(M_PI,16);
// force the constant to be long double. This prints OK
test_printDoubleln(3.14159265358979323846264338L,16);
// this should be a double but in 1.6.5_r2 and later
// it obviously is rounded to a float
test_printDoubleln(3.14159265358979323846264338,16);
Serial.println("printf");
Serial.printf("%18.16lf\n",3.14159265358979323846264338L);
Serial.printf("%18.16lf\n",3.14159265358979323846264338);
}
void loop()
{
}
This code runs correctly with Arduino versions 1.0.6, 1.6.1 and 1.6.4
Anyone run into this?
Am I forgetting a magic incantation or is something up the creek?
Pete
Last edited: