1

I would like to get my Arduino to print milliseconds as decimals of seconds. For example, instead of printing 10456, printing 10.456

I have tried to set a float value or millis, and dividing by 1000 but that gives me a whole number "0" to "1" which is not ideal

The code I use is:

float ourtime = (millis() / 1000);  
dataFile.print (ourtime);

What would be the easiest, and least taxing (for the processor) way of getting a script to return milliseconds as seconds while taxing the unit as little as possible ?

  • Could you make explicit the meaning of "the best way"? Do you mean, a way that works properly, a way with readable code, a fast way (how fast), a way is as little code as possible?... – jfpoilpret Feb 25 '17 at 7:25
2

Dividing an integer by an integer (1000) yields an integer, regardless of what you cast that result to.

Instead you need to divide the integer by a float, or cast the integer to a float first. The simplest way is:

Serial.println(millis() / 1000.0, 3);

The 3 as the second parameter tells Serial.println() how many decimal places to print. The .0 at the end of 1000.0 forces the compiler to see 1000 as a float value (you can also use 1000f if you prefer).

Since floating point can be quite expensive it's possible to emulate it using integer division and modulus (although they are still quite expensive):

uint32_t now = millis();
uint32_t secs = now / 1000;
uint32_t ms = now % 1000;

Serial.print(now / 1000);
Serial.print(".");
if (ms < 100) Serial.print("0");
if (ms < 10) Serial.print("0");
Serial.println(ms); 

You could replace the % with a x1000 and a subtraction too if you like:

uint32_t now = millis();
uint32_t secs = now / 1000;
uint32_t ms = now - (secs * 1000);

Serial.print(now / 1000);
Serial.print(".");
if (ms < 100) Serial.print("0");
if (ms < 10) Serial.print("0");
Serial.println(ms); 

A further method would be to first "print" the milliseconds to a string, then split it into two chunks. Print the first chunk, then a decimal point, and finally the second chunk. This avoids the use of any division or multiplication, but does use expensive string operations:

char num[10];
snprintf(num, 10, "%lu", millis());
uint8_t nlen = strlen(num);

for (uint8_t i = 0; i < nlen - 3; i++) {
    Serial.write(num[i]);
}
Serial.write('.');
for (uint8_t i = nlen-3; i < nlen; i++) {
    Serial.write(num[i]);
}
Serial.println();

So really there is no "perfect" method for converting milliseconds into seconds that uses hardly any CPU cycles. All base 10 operations tend to be a bit expensive on a small microcontroller without hardware support for integer MUL/DIV instructions, or a Floating Point Unit.

| improve this answer | |
  • You mean if (ms < 100) and if (ms < 10). Integer division is not cheap either: I timed your float code version at 11480 CPU cycles (not counting the call to millis()) and you int version at 7561 cycles, i.e. 34% less time. Both for the millis value 12345678UL. So it's faster, but not by orders of magnitude. – Edgar Bonet Feb 24 '17 at 21:10
  • You are right, my keyboard is hiding under the desk at the moment and I can't actually see it. No division on an AVR is cheap. If you are that concerned about the speed of division you would pick an MCU with a hardware division unit, or an FPU and use floating point. – Majenko Feb 24 '17 at 21:13
  • Serial.println(millis() / 1000.0, 3); is a great idea, but if used alone, it drops zeros. So the output would be "0.849", "0.874", "0.95" (and not "0.950") which may cause problems. – AhBajeezus Feb 24 '17 at 21:15
  • Sure. I am just saying that replacing floating point division by integer division does not provide as large a speedup as one may naively expect. – Edgar Bonet Feb 24 '17 at 21:17
  • Also you have to wonder how much of the difference is due to the serial print routines, one handling float and the other handling integers... – Majenko Feb 24 '17 at 21:19
0

Division is quite expensive.

Division can be done with subtraction, or shifting - right shift 10 (= /1024) and then compensate for that 24....

You may need too benchmark to see if on your chip this will yield any gaiin.

edit:

I did some quick benchmarking against the brutal force approach by Majenko (division followed by the mod operation), for full range 16-bit types, dividing by 1000, on a PIC16, XC8 free mode:

for 16-bit types 1. Majenko's approach (1000 divisor + mod): 45.9M ticks; 2. revised Majenko's approach (1000 divisor, no mod): 36.9M ticks; 3. My approach (1024 divisor, no mod): 20.8M ticks.

[edit 2]for 32-bit types 1. Majenko's approach (1000 divisor + mod): 101.6M ticks; 2. revised Majenko's approach (1000 divisor, no mod): 63.0M ticks; 3. My approach (1024 divisor, no mod): 19.3M ticks.

the same trend applies under the pro mode.

the same trend applies for AVR, with the speed gain being bigger.

edit 2: 32-bit benchmarking added.

I also revised my approach for 32-bit types to speed up the execution a little.

lessons learned:

1) mod ops shall be avoided as much as you can;

2) divisions shall be avoided as much as you can.

3) algorithm rules!

however, be cautious to extrapolate the data here. on a chip with hardware multiplier + divider (STM8, my favorite chip), this trend may not apply and you should benchmark on the target chip to be safe.

| improve this answer | |
  • Care to show the algorithm to “compensate for that 24”? – Edgar Bonet Feb 24 '17 at 20:26
  • 1
    I don't think it the division that is slow, but the use of a float on a CPU that doesn't support floating point operations. – Gerben Feb 24 '17 at 21:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.