Dealing with very small numbers?

I'm measuring current and want to calculate and continuously display Amp Hours.

The problem is that I'm measuring milliseconds and need to convert to hours, and float can't deal with the maths.

I'm accumulating the AH as follows:

total_amp_hours = total_amp_hours + current*(float(period/2160000000));

Where 'current' is typically < 1A and 'period' is around 2000 milliseconds. 2160000000 is the number of milliseconds in an hour. The problem arises because 2000/2160000000 equates to zero with float.

I guess I could use a much longer time period, but would lose precision with current changes during the 'slot'.

I'd welcome any ideas to help me with the problem, please!

Jim

Several solutions are possible:

There was a NIck Gammon who implemented the big number class which can also do very small numbers

Performance is traded for precision !

You could do the math right as it contains an error

total_amp_hours = total_amp_hours + current*(float(period/2160000000));

==> total_amp_hours = total_amp_hours + current * (1.0 * period)/2.16e9);

Final option:
Add the amps per second in an intermediate variable and when a second has passed add its value to the amp_hour

loop()
{
total_amp_second += current * (1.0* period/2.16e9));
if (second has passed)
{
total_amp_hour += total_amp_second;
total_amp_second = 0.0;
Serial.println(total_amp_hour);
}
}

2160000000 is the number of milliseconds in an hour.

In my days there were 3.600.000 milliseconds in an hour, but times seem to go fast in the internet era :wink:

If total_amp_hours is an integer, the roundoff lost by adding a small floating point number to an integer is going to be quite substantial.
E.G. if the new amount is 1.95 Ah then you will only add 1 to total_amp_hours.
You would be better off declaring total_amp_hours as a float and when it is being displayed, round it off to an integer.

float total_amp_hours;

total_amp_hours += current*period/3600000.0;
integer_amp_hours = total_amp_hours + 0.5;
// then display integer_amp_hours

Pete

jimford:
The problem arises because 2000/2160000000 equates to zero with float.

I suspect the problem is that you aren't actually using float values for all your intermediate values.

2000/21600000000 equals integer 0, if you assign it to a float you get 0.0.

But 2000.0/2160000000.0 equals a small non-zero float number.

(Not the number you wanted if you thought there were 21600000000 milliseconds in an hour, but not zero.)

I'm not certain I completely understand your problem. If calculating in msec means working with a too small number, then why not calculate in microseconds and scale back (or not) to milliseconds in your calculated result?

Agree with PeterH. I'd say total_amp_hours needs to be typed as float for sure. If current and period are not floats, then coerce them as follows and all should be well.

    total_amp_hours += float(current) * float(period) / 3.6e6;

(Last I checked, there are 3,600,000 milliseconds per hour.)

There is a very simple answer: When you measure to small scale, use smaller rulers.

Instead of units in Amp hours, use micro-Amp or pico-Amp hours and do your math in 32 or 64 bit integers. That will give you 6 to 9 digits right of Amp hours decimal, 32 bit integer will give you that from +2 to -2A. If you're going to need roots, trig or logs, either use a table in PROGMEM or get a finite series formula into a function, except for power of 2 operations. It should still be faster than FP as well as more accurate. Need more accuracy, use nano-Amp hour units. They still add up to Amps, just more zeros.

Look at how your multimeter ranges work....

robtillaart:
There was a NIck Gammon who implemented...

There still is a Nick Gammon. :wink:

Thanks for all the helpful replies. I think I've enough to work on now.

It was pretty stupid of me to say that there are 2160000000 milliseconds in an hour! The only excuse I can offer is that I was befuddled by the number of zeros accumulating and multiplied by an extra 60!

Thanks again.

Jim

You use milliseconds to measure to the millisecond in integer. And the result is always faster and correct.