NerdKits - electronics education for a digital generation

You are not logged in. [log in]

NEW: Learning electronics? Ask your questions on the new Electronics Questions & Answers site hosted by CircuitLab.

Microcontroller Programming » Quick question for those fluent in C

June 06, 2010
by Rick_S
Rick_S's Avatar

I have a quick question for those fluent in C. In a program I wrote, I use type casting (I think thats the right verbage??) to get just the integer portion of a math function. For example in this program posted in another topic on the forum I do the following.

First I declare my variables as the type I want.

uint8_t ones, tens, hund, thou;
uint16_t cnt, temp;

And then later in the program I do the following math (cnt can be any value between 0 and 9999)

ones = cnt%10;
temp = cnt/10;
tens = temp%10;
temp = temp/10;
hund = temp%10;
thou = temp/10;

This way, if cnt = 5320, ones becomes 0, tens becomes 2, hund becomes 3 and thou becomes 5.

Is this a proper way to do this type of parsing or can this cause other problems? Is there a better way?

Thanks,

Rick

June 06, 2010
by mrobbins
(NerdKits Staff)

mrobbins's Avatar

Hi Rick,

That's a perfectly fine way of doing it -- I don't think there's any "better" way of converting from a (binary) integer field to a bunch of decimal digits.

Just as an academic curiousity, if you wanted to save a few CPU cycles, you could probably replace all of the / lines with subtractions:

ones = cnt%10;
temp = cnt - ones;
tens = temp%10;
temp = temp - tens;
hund = temp%10;
thou = temp - hund;

This will be faster because the AVR has built-in instructions for addition, subtraction, and multiplication, and these can be done in 1 or 2 clock cycles (for 8-bit fields). In contrast, division and the % operator have to be performed with a bunch of operations. But the result will be the same, and you're probably not really pressed to save such few cycles!

If you want another example, the "lcd_write_int16" function in the libnerdkits/lcd.c file is an example of a slightly more complicated version of this idea. It has to deal with variable-decimal-length numbers, as well as negative numbers, but it's basically doing the same thing as your code.

In summary, your code is just fine!

Mike

June 06, 2010
by Rick_S
Rick_S's Avatar

If I did the subtractions though, it wouldn't shift the digits would it? In my examble above, if cnt = 5320 wouldn't it work as follows?

ones = cnt%10;      - ones = 0
temp = cnt - ones;  - temp = 5320
tens = temp%10;     - tens = 0
temp = cnt - tens;  - temp = 5320
hund = temp%10;     - hund = 0
temp = cnt - hund;  - temp = 5320
thou = temp%10;     - thou = 0

Or am I seeing this wrong? Is there a better way to drop a digit without division?

June 06, 2010
by mrobbins
(NerdKits Staff)

mrobbins's Avatar

Hi Rick,

You're right -- I got turned around somewhere! How about:

thou = cnt/1000;
temp = cnt - 1000*((uint16_t) thou);
hund = temp/100;
temp = temp - 100*((uint16_t) hund);
tens = temp/10;
ones = temp - 10*(tens);

This one has 3 divisions, 3 multiplications, and 3 subtractions. Your original one had 3 divisions and 3 modulo (%) operations (which I believe to be about as expensive as division). Just for reference, a 16x16 bit multiplication takes roughly 15 to 20 clock cycles with the hardware multiplier, while a 16/16 bit division might take roughly 150-200 clock cycles. That's a big difference, so when speed counts, avoid division! (The important exception is that dividing by any power of 2 is "cheap" because it's really just a shift in binary.)

But in any case, your original code works, and more importantly you understand it, so just stick with it until you really need the speed! Sorry for the confusion.

Mike

June 06, 2010
by Rick_S
Rick_S's Avatar

No problem at all, I was just excited to figure out the code to drive the 595 shift registers relativly painlessly.

Also, I didn't realise there was such a difference between Multiplication and division Clock cycle wise.

One last thing,

When you wrote the line

temp = cnt - 1000*((uint16_t) thou);

does that temporarily cast the variable thou as 16 bit on the fly? And if so, why is it necessary? Does something bad happen if variables of mixed types are used in an operation? Or does that ensure that the compiler will do 16 bit math?

Rick

June 06, 2010
by mrobbins
(NerdKits Staff)

mrobbins's Avatar

Hi Rick,

I'm just in the habit of explicitly specifying when I want the compiler to interpret something as a larger bit width because I've occasionally been burned in the past when the compiler makes the wrong assumption. I think it's almost certainly going to use 16-bit math in this case (even without my cast to uint16_t), since the constant parameter is >255 and the result is going into a 16-bit field, but I've just gotten into the habit of doing this explicit casting when mixing different size fields in arithmetic.

Mike

June 13, 2010
by BobaMosfet
BobaMosfet's Avatar

mrobbins-

I'm with you on that one. I learned a long time ago to be very careful with compilers and optimization options. I've even run into problems where low-level debuggers (we called them 'monitors'-- If you've ever heard the statement 'break into the monitor', that's what it means) incorrectly altered object-code before letting it run on a CPU.

BM

Post a Reply

Please log in to post a reply.

Did you know that our USB NerdKit works on Windows, Linux, and Mac OS X? Learn more...