0
\$\begingroup\$

I'm trying to get my head around integer division. I'm entering a decimal number between 100 and 6300 into my program, I then need to scale this number by 100 and output the result as a 6-bit number.

Mathematically what I'm trying to do is:

Output = Input/100

Very simple! But I just can't seem to get this to work in C. I'm using MikroC for a PIC18 MCU.

Example code:

int input;
int output;

void main(){
TRISA = 0x00;
input = 1000;//operator has input 1000
output = input/100.0;//scale input by 100
PORTA = output;//output 6-bit number
}
\$\endgroup\$
  • 1
    \$\begingroup\$ Can you show the code you tried? What did you expect it to do? What did it actually do? \$\endgroup\$ – The Photon Apr 22 '16 at 5:05
  • \$\begingroup\$ I notice input is defined as an int, but you are dividing by a float 100.0. What does your particular compiler do with mixed mode arithmetic? \$\endgroup\$ – Neil_UK Apr 22 '16 at 5:24
  • \$\begingroup\$ If I watch the output variable with the above code, output = 0 \$\endgroup\$ – Gozmit Apr 22 '16 at 5:26
  • 1
    \$\begingroup\$ I have used PIC compilers where an int was 8 bits. Don't know about this one. \$\endgroup\$ – gbarry Apr 22 '16 at 5:30
  • \$\begingroup\$ The "input" is a variable defined, or is it nd input from external world \$\endgroup\$ – seetharaman Apr 22 '16 at 6:41
2
\$\begingroup\$

As far as I'm concerned, there's no need for float division here at all, if you're just going to cast the result to an integer and write to a port. The result of your float division will be truncated to yield an integer, so you might do integer arithmetic.

unsigned int input;
unsigned char output;

void main(){
  TRISA = 0x00;
  input = 1000;//operator has input 1000
  output = input/100;//scale input by 100
  PORTA = output;//output 6-bit number

  while (1);  //while away once the program is done
}

For implementation independence, you can include stdint.h and use uint8_t and uint16_t to define output and input respectively, though thats hardly necessary here. If you do want the benefits of float division, you have to round.

#include <math.h>

unsigned int input;
unsigned char output;

void main(){
  TRISA = 0x00;
  input = 1000;
  output = round((double)input/100.0);  // implicit cast to uchar
  PORTA = output;

  while (1);  //while away once the program is done
}

I should mention that this latter approach is more costly than the former, since its floating point arithmetic. Use this only if you need the additional accuracy.

EDIT:

A compromise would be to add an offset to input and then dividing; this increases the accuracy of your results by simulating rounding with integer math. Its a lot more efficient than the floating point option.

unsigned int input;
unsigned char output;

void main(){
  TRISA = 0x00;
  input = 1000;
  output = (input + 50) / 100; 
  PORTA = output;

  while (1);  //while away once the program is done
}
\$\endgroup\$
  • \$\begingroup\$ Yes, you are right. But I meant to continue by doing a round function, after asking if the accuracy was important. I wanted to get the casts (sorry transistor, spelling checker has been updated) working first. \$\endgroup\$ – Mark Apr 22 '16 at 23:31
  • \$\begingroup\$ To add, floating point has miserable overhead in a PIC. \$\endgroup\$ – Mark Apr 22 '16 at 23:42
  • \$\begingroup\$ @Mark I know. Its his choice, depends on the needed accuracy. \$\endgroup\$ – TisteAndii Apr 22 '16 at 23:46
  • 1
    \$\begingroup\$ Here's an alternate method: I usually add 1/2 the divisor to get correct rounding. That line of code would be "output = (unsigned char)((input + 50)/100);" So that input=150 would become 200/100 = 2 instead of 1 in your original code. \$\endgroup\$ – Vince Patron Apr 22 '16 at 23:52
  • \$\begingroup\$ The answer from TisteAndii is working! I don't need float point accuracy. \$\endgroup\$ – Gozmit Apr 23 '16 at 0:06
1
\$\begingroup\$

Your code should have generated a lot of warning messages, specifically about missing casts. Your calculation should be more explicit, and look like this:

output = (int)( (float)input/100.0 ); // a more explicit operation

However, "output" should have been defined as a "short" if the result is only six bits. Since "PORTA" is only 8 bits and "output" is 16 bits, the compiler probably stored the high byte of "output" (a zero) into "PORTA" and stored the low byte (the actual result) in the memory location following "PORTA".

output = (short)( (float)input/100.0 ); // "output" needs to be short

\$\endgroup\$
  • \$\begingroup\$ output = short( (float)input/100.0 ); Does this mean I declare output as a short and input as a float, or does the actual code look like that? \$\endgroup\$ – Gozmit Apr 22 '16 at 7:49
  • \$\begingroup\$ You do need to define "output" as a "short". But you don't need to define "input" as a "float" - the caste will take care of the conversion for you. You should define "input" as whatever is appropriate for getting its value into the program in the first place. \$\endgroup\$ – Mark Apr 22 '16 at 7:55
  • \$\begingroup\$ Excuse my ignorance Mark, but what is a caste? \$\endgroup\$ – Gozmit Apr 22 '16 at 7:59
  • \$\begingroup\$ A caste is when you explicitly convert (caste) a variable of one type to another type. "input" is an in "int", but "(float)input" is a float. "(float)input/100.0" is a "float", but "short((float)input/100.0)" is a "short". \$\endgroup\$ – Mark Apr 22 '16 at 8:14
  • \$\begingroup\$ I can't get that to work in MikroC \$\endgroup\$ – Gozmit Apr 22 '16 at 8:51

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.