Forcing Serial.print() to give me two digits

Hi all,

I need to print floats and ints in the Serial Monitor, and I would prefer that it always be two digits left of the decimal point in the cases of floats, and always two digits for ints. That is:

04 instead of 4
02.8549 instead of 2.8549
00.9650 instead of 0.9650

How do I accomplish this? I would prefer not to make it needlessly complicated, all I'm doing is converting millis() into HH:MM:SS.SSSS.

EDIT: problem solved. Solution presented for reference.

void maketime(int _T) // pass millis() to this argument.
{
  int H = _T / (3600000);
  int M = (_T % (3600000)) / (60000);
  float S = (float)((_T % 3600000) % (60000)) / 1000.0;
  Serial.print(H);
  Serial.print(":");
  if(M < 10)
  {
    Serial.print("0");
  }
  Serial.print(M);
  Serial.print(":");
  if(S < 10.0)
  {
    Serial.print("0");
  }
  Serial.print(S,4);
}

If the output value is less than 10, print a "0". After that test, print the output value.

tmd3:
If the output value is less than 10, print a "0". After that test, print the output value.

This is what I ended up doing. I wanted to avoid unnecessary floating-point calculations, but I compromised.

  int H = _T / (3600000);

Literal constants are ints, unless you explicitly state otherwise. 3600000 is not a valid int value.

The parentheses add nothing.

That should be:

  int H = _T / 3600000UL;

I need to print floats and ints in the Serial Monitor, and I would prefer that it always be two digits left of the decimal point in the cases of floats, and always two digits for ints.

So, why are you asking for 4 decimal places?

  Serial.print(S,4);

Since you have whole milliseconds to begin with, why do any float calculations at all? Grab MS = milliseconds by taking _T % 1000, set = _T / 1000, and take it from there. Get H, M, S and MS as integers and print them. I don't see that doing long divides or mods is worse than doing the same calculations in floating point. Check the possible ranges of your variables, and get the type right - some of them will overflow as int, and should be long. Because your variables won't be negative, you can type them as unsigned.

PaulS is right - 3600000 is a long value, and it needs a long qualifier, though, in IDE 1.0.1, it seemed to work for me without it. He's also right to question why you want four digits after the decimal, since the input argument is in whole milliseconds, and it has only three significant digits after the decimal expressed as seconds. The last digit will always be zero. If you see something else, it's likely a rounding error from all those float calculations.

PaulS:

  int H = _T / (3600000);

Literal constants are ints, unless you explicitly state otherwise. 3600000 is not a valid int value.

The parentheses add nothing.

That should be:

  int H = _T / 3600000UL;

Thanks, I've made this adjustment, although I haven't been noticing any errors when using 3600000 as I've written it (IDE 1.0.1).

I need to print floats and ints in the Serial Monitor, and I would prefer that it always be two digits left of the decimal point in the cases of floats, and always two digits for ints.

So, why are you asking for 4 decimal places?

  Serial.print(S,4);

Because I still need millisecond precision. I need the format to be HH:MM:SS.SSSS.

EDIT: realized I'm only gonna get SS.SSS. Adjustment made.

EDITEDIT: After making a few adjustments and realizing that I was essentially passing millis() to my maketime() function, I've settled out the actual code needed to make this work:

void maketime(unsigned long _T)
{
  int H = _T / 3600000UL;
  int M = (_T % 3600000UL) / 60000;
  float S = (float)((_T % 3600000UL) % 60000) / 1000.0;
  if(H < 10)
  {
    Serial.print("0");
  }
  Serial.print(H);
  Serial.print(":");
  if(M < 10)
  {
    Serial.print("0");
  }
  Serial.print(M);
  Serial.print(":");
  if(S < 10.0)
  {
    Serial.print("0");
  }
  Serial.print(S,1);
}

I reduced the accuracy of the mantissa in the seconds because that will make it easier for me to correlate timestamps across several Arduinos. In the final code, all Serial statements are changed to file writing statements, and the instruments are on the bench for testing. In an hour or so I'll check to see if it's working, but preliminary tests suggest that it works fine.

EDIT: realized I'm only gonna get SS.SSS. Adjustment made.

So, you are happy now?

I misread the part about two decimal places to the left. Most people ask about how to get more to the right.

PaulS:

EDIT: realized I'm only gonna get SS.SSS. Adjustment made.

So, you are happy now?

I misread the part about two decimal places to the left. Most people ask about how to get more to the right.

Much happier. The idea behind this problem is that I have an instrument running two separate microcontrollers, an Arduino and a ChipKit UNO. Both have a millis() function. The ChipKit is governing various sensors while the Arduino acts as a sort of "command module", running a GPS and collecting various other readings. Since the readings need to come across temporally and spatially correlated, I need to reconcile all sensor readings with the timestamp from the GPS. Originally I was doing this with a basic millis() timestamp, but found that to be a headache. It's a PITA to have to recalculate thousands of readings to convert milliseconds into HH:MM:SS.S, so I wanted this function to do it for me. Now it's much more "human-readable", as it were.

You might investigate using the sprintf() function [which is available in
the compiler libraries] to preformat your strings into a char buffer[], and
send them out using Serial.print(buffer). Then you can use all of the
normal formatting options available with the regular C-language printf(),
I think.

You might investigate using the sprintf() function

Except that the sprintf() function, as implemented on the Arduino, doesn't support the %f format for floats.

Except that the sprintf() function, as implemented on the Arduino, doesn't support the %f format for floats.

Hmm, I've only used it for non-floats - few floats in my programs.

But that seems bizarre. I'm sure the underlying C compiler must have originally had
a full sprintf() implementation. Why on earth would they purposely hamstring it?

Because handling float formatting drastically increases the size of the sprintf() function. Since floats are not the Arduino's strong suit, compromises were made that try to balance the needs of everyone.