Rounding Problems Converting Hours Minutes Seconds (H.MS) to Hours (H.HH)

Hello all!

I'm trying to implement a function hms->s (hh.mmss -> h.hhhh) and vice versa.

Unfortunately it seems that the arduino does not exactly represent integers as float variables - maybe some rounding option can help?

My code so far is:

float hmstoh(float hms) { // hms->h
  return((int)hms+((int)(100*(hms-(int)hms)))/60.0+(100*(100*hms-(int)(100*hms)))/3600.0);
}

float htohms(float h) { // h->hms
  return((int)h+((int)(60*(h-(int)h)))/100.0+(60*((60*(h-(int)h))-(int)(60*(h-(int)h))))/10000.0);
}

Example:
Converting 16.11 (16 hours and 11 minutes) to hours shows 16.1944 (instead of 16.1833).
There seems to be a problem to convert 0 seconds.
Adding one second works fine: 16.1101 shows the right value (16.1836).

Especially the part

+(100*(100*hms-(int)(100*hms)))/3600.0)

seems to need some improvement.

Thanks for any help and hint.

Regards
deetee

Hello,
Arduino is definitely not alone in that respect. I can remember when MSSQL would respond to SELECT 1 with 0.9997...

I tried this in Excel and got similar results.

I think the problem is that because you say "(int)" that everything is being converted to an integer too soon and therefore things are lost in the rounding.

I think you would be better off leaving the minutes/seconds as a float and convert to int after dividing by 3600.

Why are you using float to represent essentially integer values?

I would start by converting hh.mmss by adding 1/2 second (0.00005) and multiplying by 10000 before truncating to an integer. That should get you a fairly clean six digit unsigned long integer. Then you can split the number up into hh, mm, and ss. Convert to seconds and divide by seconds per hour to get hh.hhhh

deetee:
Hello all!

I'm trying to implement a function hms->s (hh.mmss -> h.hhhh) and vice versa.

Why do you want this function? How will it be used?
http://mywiki.wooledge.org/XyProblem

Hello all!

I'm trying to make some kind of calculator with clock and stopwatch functions.
So the user input (i.e. to set a countdown time) comes from a float register - that's why I have to use a float input for this function.

To solve the problem I used the instructions from johnwasser (thanks very much).
To deal with tenths of a second too (stopwatch) I rounded at the sixth digit (+0.000005). I don't know why, but I have to use a long variable for that - an int doesn't work correctly.

I used the same trick (rounding at digit six) for the inversion (htohms) to ensure to convert back (reasonably) exactly.

float hmstoh(float hms) { // hms->h
  unsigned long T=(hms+0.000005)*100000;
  int hh=(int)(T/100000),mm=(int)(T/1000)-hh*100,ss=T/10-hh*10000-mm*100,t=T-hh*100000-mm*1000-ss*10;
  return(hh+mm/60.0+ss/3600.0+t/36000.0);
}

float htohms(float h) { // h->hms
  h+=0.000005;
  return((int)h+((int)(60*(h-(int)h)))/100.0+(60*((60*(h-(int)h))-(int)(60*(h-(int)h))))/10000.0);
}

Now it works perfectly - at least for the few examples I tested (maybe that's why I'm not completely satisfied yet).
Thanks again for your help.

Regards
deetee

So the user input (i.e. to set a countdown time) comes from a float register - that's why I have to use a float input for this function.

If you change your input function to return an unsigned long (a whole number of seconds) instead of a float, this "problem" simply disappears.

deetee:
To solve the problem I used the instructions from johnwasser (thanks very much).
To deal with tenths of a second too (stopwatch) I rounded at the sixth digit (+0.000005). I don't know why, but I have to use a long variable for that - an int doesn't work correctly.

An int can only handle numbers up to 32767. A long can handle up to about 2 billion. Probably something in your calculation was causing the numbers to exceed 32767.

What kind of calculator are you trying to make? Arduino floats only give you 7 or sometimes 8 good digits. Is that enough for you?

Please take a look at: http://floating-point-gui.de/

Hello!

Thanks for enlightning my long-int-problem ... of course some hours of time converted to tenth of seconds exceed the int-boundary.

I'm a HP calculator fan. One of the most powerful traditional RPN calculators is the WP34s. Surprisingly the software doesn't come from HP but is open source made by some HP-lovers (Paul, Walter, Marcus, ...).

My calculator is almost finished and some kind of "poor people's WP34s" with a lot of math functions (trigonometrics, hyperbolics, statistics, normal distribution, regression, conversions, clock, ...). It's based on an arduino (nano) with an excellent OLED-display (128x64) and a selfmade 4x3-keyboard. Right now I'm doing some miniaturizing and providing some additional battery powering.

Of course an arduino is an 8-bit-calculator with 5 to 6 digits accuracy. But that is (in my opinion) really enough to make a scientific calculator. But yes, that's not capable to add billion-numbers with cent-accuracy.

Regards
deetee

I guess you and I want different things from calculators. I like lots of digits. You like lots of features.

I hope that the algorithms you are using are numerically stable enough that they do not eat up precision that you cannot afford to lose.

What version of Nano are you using? Apparently, there is more than one version.

Of course an arduino is an 8-bit-calculator with 5 to 6 digits accuracy. But that is (in my opinion) really enough to make a scientific calculator. But yes, that's not capable to add billion-numbers with cent-accuracy.

Most early calculators had, at best, four bit processors.