Pages: [1]   Go Down
Author Topic: Math.h log10 function bug? [not a bug finally]  (Read 1544 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Newbie
*
Karma: 0
Posts: 20
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I'm developing with the Arduino UNO a device that uses a TSL238 light-to-frequency photodiode to measure light.

Just because in astronomy the 'standard' is a negative logaritmic one unit, I use log10 function (from math.h) here to pass from Hz (output of the TSL237, measured with pulseIn as the pulses are pretty slow for FreqCounter and FreqPeriod) to the logaritmic unit system with an offset of 22.0 .

magnitude = 22.0-2.5*log10(frq);

But something isn't ok with that simple operation.

1.03hz => 21.970mag
1.01hz => 21.994mag (that's OK, decreasing frequency means higher magnitude)
0.92hz => 22.91 (that doesn't seem to be correct. I expected 22.091)
0.91hz => 22.98 (also incorrect!)
0.78hz => 22.272 (correct!)
0.64hz => 22.480 (correct!)

What is wrong in the 22.0-2.5*log10(0.92) and 22.0-2.5*log10(0.91) ?

Update. Maybe it's losing a digit between . and 9?. I mean 22.091 and 22.098 sounds pretty accurate. I'm using the printDouble function from here: http://www.getbugged.com/2008/11/22/how-to-serialprint-a-double-on-an-arduino/ (original version wrote by Mem here in the Arduino forums, http://arduino.cc/forum/index.php/topic,44216.0.html) to get a bit more decimals.

Thanks in advance,
Minaya
« Last Edit: January 15, 2013, 07:08:30 pm by minaya » Logged

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

I'm pretty sure it's a bug in printDouble function (at least in the version I used).

I've simulated it in python

Code:
import sys

val = float(sys.argv[1]);
precision = int(sys.argv[2])

sys.stdout.write(str(int(val)))
sys.stdout.write('.')

if val>0:
frac = (val-int(val))*precision
else:
frac = (int(val)-val)*precision

frac1 = frac
while (frac1/10!=0):
frac1 = frac1/10
if frac1!=0:
precision=precision/10

while (precision/10!=0):
precision = precision/10
sys.stdout.write('0')

sys.stdout.write(str(int(frac))+'\n')

If I write python buggyprintDouble.py 1.057 1000 it says 1.56,
but 1.157 1000 says 1.157

My first try to fix it,

Code:
import sys

val = float(sys.argv[1]);
precision = int(sys.argv[2])

sys.stdout.write(str(int(val)))
sys.stdout.write('.')

if val>0:
frac = (val-int(val))*precision
else:
frac = (int(val)-val)*precision

lfact = 10
while int(lfact*(val-int(val)))==0 and lfact<precision:
lfact*=10
sys.stdout.write('0')

frac1 = frac
while (frac1/10!=0):
frac1 = frac1/10
if frac1!=0:
precision=precision/10

sys.stdout.write(str(int(frac))+'\n')

That's, commet the while(precision/10!=0) bucle and add before a custom lfact correction (just for printing the left zeros).

Now python newprintDouble 1.057 1000 says 1.056 and python newprintDouble 1.157 1000 says 1.057.

Lets traslate it to C++  smiley-cool

« Last Edit: January 15, 2013, 07:08:09 pm by minaya » Logged

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

Based on the last version published by mem (which uses byte precision)

Code:
void printDouble( double val, byte precision){
  // prints val with number of decimal places determine by precision
  // precision is a number from 0 to 6 indicating the desired decimial places
  // example: printDouble( 3.1415, 2); // prints 3.14 (two decimal places)

  Serial.print (int(val));  //prints the int part
  if( precision > 0) {
    Serial.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 lfact = 10;
    while(int(lfact*(val-int(val)))==0 && lfact<mult){
      lfact *= 10;
      Serial.print("0");
    }

    unsigned long frac1 = frac;
    while( frac1 /= 10 )
      padding--;
    Serial.print(frac,DEC) ;
  }
}

I need to test extensively, but the first measures seems to be pretty ok.
Logged

Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 223
Posts: 13876
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

It seems to be losing at least the first leading zero of the decimal part.


In Arduino you can use:  Serial.println(floatnumber, decimalplaces);   e.g. Serial.print(3.14159265, 3);

afaik it works correctly...

can you give it a try in your sketch?


Logged

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

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

It's working as expected with print(...,3) robtillaart.

Thanks!
Logged

Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 223
Posts: 13876
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Can you post the final sketch? for future reference?

What you may like is the number always to be 4 digits e.g.  12.12 but 1.212   

Code:
Serial.print(mag, mag<10?3:2);  // using an if then else construct (the cryptic one) to determine the #digits.

which is functional equivalent to

Code:
if (mag<10) Serial.print(mag, 3);
else Serial.print(mag, 2);

Logged

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Pages: [1]   Go Up
Jump to: