Sunday, May 13, 2012

C operators

    One of the great things to do with numbers is mathematics.  Fortunately, C allows arithmetic to be performed on variables and provides some basic operators to perform simple arithmetic. There are also some less intuitive operators which make use of the binary version of a variable.  We'll begin with standard mathematics, discuss a cool operator called modulo, and finish with bitwise operators.

Arithmetic:
    In C, the operators for arithmetic work very similar to their real world counterparts.  For example, the + operator requires a number on the left and right and creates a number which is the sum of the left and right numbers.  The same applies to the - operator, but * is used for for multiplication and / for division.

    Here is an example to demonstrate some mathematical operations:
#include <stdio.h>

int main() {
    //declare variables
    int a = 5, b = 7, c;

    //create variables for real numbers
    double d = a, e = b, f;
    //here, d is the double version of a's value

    //add and place in c, print
    c = a + b;
    printf("%d + %d = %d\n", a, b, c);

    //subtract and place in c, print
    c = a - b;
    printf("%d - %d = %d\n", a, b, c);

    //multiply and place in c, print
    c = a * b;
    printf("%d * %d = %d\n", a, b, c);

    //integer divide and place in c, print
    c = a / b;
    printf("%d / %d = %d (int)\n", a, b, c);
    //integer divisions are always rounded down

    //real divide and place in f, print
    f = d / e;
    printf("%lg / %lg = %lg\n", d, e, f);
    //real divisions work with two real numbers

    getchar();
    return 0;
}
output in prompt:
5 + 7 = 12
5 - 7 = -2
5 * 7 = 35
5 / 7 = 0 (int)
5 / 7 = 0.714286


    Notice that the expression 5 / 7 creates different numbers for integers and doubles.  If both operands, 5 and 7 in this case, are integer types, the result of their division is rounded down to the nearest whole number.  However, if the operands are floats or doubles, the outcome will be of the same type and not rounded down.

    Doubles can also be initialized as an integer value in a variable.  This example provides d = a to show that double d can be given the value of a, except in real number form.  You can also use a = d to convert a real number d to an integer a, but like division, the result is rounded down.

Modulo:
    Another operator present in C is modulo.  In other languages, this operator is used with the word mod, but for C, the symbol % is used.  This operation applies to integers and returns the remainder of a division.

    In a sense, the remainder is the part lost when integer division is used instead of real division.  The remainder is the numerator of the fractional part of a division.  For example, the real division 7/5 equals the mixed number 1 + 2/5, so integer division 7/5 returns 1 and 7%5 returns 2 (from the 2/5 fraction).

    One interesting property of modulo is that it will produce a number closer to zero than the denominator.  A positive number modulo 5, for example, will produce a number from zero to four without exception.

    Also, if the positive number continues to increase by one, the positive number modulo 5 will loop through 0 to 4 repeatedly.  Here is a table to show the cycle:
numbernumber % 5calculations
000 / 5 = 0 + 0/5
111 / 5 = 0 + 1/5
222 / 5 = 0 + 2/5
333 / 5 = 0 + 3/5
444 / 5 = 0 + 4/5
505 / 5 = 1 + 0/5
616 / 5 = 1 + 1/5
727 / 5 = 1 + 2/5
838 / 5 = 1 + 3/5
949 / 5 = 1 + 4/5
10010/ 5 = 2 + 0/5

    Notice that the division increases from 0 to 2 during this cycle and the modulo loops through 0 to 4 twice then back to 0.  To wrap up discussion of modulo, here is an example using modulo 10:
#include <stdio.h>

int main() {
    //declare variables
    int number = 19;
    int ones_place, tens_place;

    /*for numbers greater than 0, modulo 10
    calculates the ones place*/
    ones_place = number % 10;
    /*for numbers between 0 and 99, division
    by 10 gets the tens place*/
    tens_place = number / 10;

    printf("for integer %d:\n", number);
    printf("%d/10 = %d\n", number, tens_place);
    printf("%d%%10 = %d\n", number, ones_place);
    //place the numbers side by side
    printf("combined = %d%d\n",
        tens_place, ones_place);
    printf("\n");

    //increase value of number by one
    number = number + 1;
    ones_place = number % 10;
    tens_place = number / 10;

    printf("for integer %d:\n", number);
    printf("%d/10 = %d\n", number, tens_place);
    printf("%d%%10 = %d\n", number, ones_place);
    printf("combined = %d%d\n",
        tens_place, ones_place);
    printf("\n");

    ones_place = 1;
    tens_place = 2;
    /*Here, the reverse calculation is performed
    to show that modulo allows integer division
    to be reversed*/
    number = 10 * tens_place + ones_place;

    printf("for integer %d:\n", number);
    printf("%d/10 = %d\n", number, tens_place);
    printf("%d%%10 = %d\n", number, ones_place);
    printf("combined = %d%d\n",
        tens_place, ones_place);

    getchar();
    return 0;
}
output in prompt:
for integer 19:
19/10 = 1
19%10 = 9
combined = 19

for integer 20:
20/10 = 2
20%10 = 0
combined = 20

for integer 21:
21/10 = 2
21%10 = 1
combined = 21

    This example aims to show that from 19 to 20, the division by ten increases by one while the modulo 10 loops back to zero.  Also, by multiplying the division by 10 (the number used to divide) and adding the modulo, we successfully rebuild the number being divided (21 in this case).

Bitwise Operators:
    If you are not familiar with binary, here is my attempt to explain it: counting using only ones and zeros.  I will elaborate, but to spare you, the reader, some time, a better explanation can be found on the page http://mathworld.wolfram.com/Binary.html.

    Start at zero, the binary representation is 0.  Now jump to one, which has binary representation 1.  This is beginning to look easy, but now we are at 2 and can only use 1 and 0.  The binary form of 2 is 10.

    Think of binary as a sum of powers of two: for two, 2 = 1 * 2^1 + 0 * 2^0 = 2 + 0 * 1 = 2.  So three will equal 11 because 3 = 1 * 2^1 + 1 * 2^0 = 2 + 1 = 3.  What about four? 4 = 2^2 = 1 * 2^2 + 0 * 2^1 + 0 * 2^0, so in binary 100.  Here is a table of 0 to 7:
decimalbinarysum of powers of two
00000*4 + 0*2 + 0*1
10010*4 + 0*2 + 1*1
20100*4 + 1*2 + 0*1
30110*4 + 1*2 + 1*1
41001*4 + 0*2 + 0*1
51011*4 + 0*2 + 1*1
61101*4 + 1*2 + 0*1
71111*4 + 1*2 + 1*1

    This table shows the eight numbers which can be represented with three bits; the next number is 8 with binary form 1000 which requires the four bits 1, 0, 0, and 0.  A bit can only be one or zero.

    Note that 2^3 = 8 and that there are eight numbers on the previous table, each with three bits.  Since 2^4 = 16, that means sixteen numbers can be made using four bits.  A char in C is 8 bits which means 2^8 = 256 possible numbers.  Since this is the smallest size in bits of the variables types, we will work with char.

    Now onto operators!  There are only six operators to discuss now: not, and, or, xor, bit-shift left, and bit-shift right.  Their symbols in C are ~, &, |, ^, <<, and >> respectively.

    Not is a simple one: every bit of the character becomes the opposite of before.  If you perform ~31, the output is -32 because 31 = 00011111 and ~31 = 11100000.  The reason it is negative is because signed numbers use a negative power of two for the highest power.  In this case, -128 + 64 + 32 = -32.  Were the character unsigned, it would be +128 + 64 + 32 = 224.

    For and, or, and xor, two chars are required and the following table will help explain what they do to each bit:
bit in charbit in otherand(&)or(|)xor(^)
00000
01011
10011
11110

    As an example,
3 & 6 = 00000011 & 00000110 = 00000010 = 2,
3 | 6 = 00000011 | 00000110 = 00000111 = 7, and
3 ^ 6 = 00000011 ^ 00000110 = 00000101 = 5

    Finally, bit-shifting operates on a single char variable by moving all of the bits left or right a specified amount of times.  To shift 5's bits left once, use 5 << 1, which equals 00000101 << 1 = 00001010 = 10.  The bits now correspond to higher powers of two, so shifting once is the rough equivalent of multiplying by two.

    Now, to bit-shift right twice, 10 >> 2 = 00001010 >> 2 = 00000010 = 2.  In this case, a one bit is lost.  This is similar to 10 / 4 which also equals 2 because it rounds down.  Here is an example which serves as a review for bitwise operators:
#include <stdio.h>

int main() {
    char a = 3, b = 6;
    char c = 31;

    printf("bitwise operators:\n");

    printf("\n");

    //and, or, and xor of a and b
    printf("%d & %d = %d\n", a, b, a & b);
    printf("%d | %d = %d\n", a, b, a | b);
    printf("%d ^ %d = %d\n", a, b, a ^ b);

    printf("\n");

    //not and bit-shift examples
    printf("~%d = %d\n", c, ~c);
    //similar to multiplication by four
    printf("%d << 2 = %d\n", c, c << 2);
    //similar to integer division by two
    printf("%d >> 1 = %d\n", c, c >> 1);

    getchar();
    return 0;
}
output in prompt:
bitwise operators:

3 & 6 = 2
3 | 6 = 7
3 ^ 6 = 5

~31 = -32
31 << 2 = 124
31 >> 1 = 15




    For more information about operators, view the page http://h30097.www3.hp.com/docs/base_doc/DOCUMENTATION/V40F_HTML/AQTLTBTE/DOCU_059.HTM.

    The next post will cover how to make custom functions.

No comments:

Post a Comment