#1,078 – The Hexadecimal Numeral System

Humans normally represent numeric values using the decimal (base 10) numeral system.  We can also represent numbers as hexadecimal, or base 16.

The hexadecimal numeral system uses 16 different digits (numeric symbols), rather than 10.  You represent a numerical value using a string of these hexadecimal (or hex) digits.  The 16 digits used are the digits 0-9 and the letters A-F.  (E.g. 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, and F).  Comparing to the decimal system, A then represents 10, B represents 11, up to F, which represents 15.  Hexadecimal constants are often written as “0x”, followed by the digits.

Moving from right to left, the digits in a base 16 system represent powers of 16 (1, 16, 256, 4096, etc. or 16^0, 16^1, 16^2, 16^3, etc).  The value of a number represented as hexadecimal is the sum of the value of each digit multiplied by the appropriate power of 16.  For example:

1078-001

As an example, the hexadecimal number 0x3A8E is equivalent to the decimal number 14,990.

1078-002

 

 

 

 

#1,077 – The Decimal Numeral System

Humans normally represent numeric values using the decimal numeral system.  Decimal is a base 10 numeral system, which means that we use ten different digits (numeric symbols) and represent a particular number using a string of these digits.  Our ten digits are: 0, 1, 2, 3, 4, 5, 6, 7, 8, and 9.

Moving from right to left, the digits in a base 10 system represent powers of 10 (1, 10, 100, etc. or 10^0, 10^1, 10^2, etc).  The value of the number is the sum of the value of each digit multiplied by the appropriate power of 10.  For example:

1077-001

As an example, the number 3829 represents: three thousand (3 x 1000), eight hundred (8 x 100), and twenty (2 x 10) nine (9 x 1).

1077-002

 

 

 

 

#1,076 – Implicit Numeric Conversions from the char Type

You can implicitly convert an instance of a char to any of the following numeric types:

ushort int, uint, longulong, float, double, or decimal

When you implicitly convert a char to a numeric value, you use the associated code point as the numeric value.  Because of this implicit conversion, you can use char instances within numeric expressions.  Below are some examples:

            int n = 'a';  // 97
            char c1 = 'a';
            n = c1;  // still 97

            char c2 = '\u1820';
            int n2 = c2;  // 6176 (0x1820)

            int delta = 'd' - 'a';  // 3
            int strangeSum = 'a' + 'b';  // 97 + 98 = 195

#1,075 – Operator Precedence Doesn’t Affect Operand Evaluation Order

Rules for operator precedence and associativity determine the order in which the operators within an expression will be evaluated.  Parentheses can also change the order in which the operators are evaluated.

For example:

            // * operator evaluated before +
            int sum1 = 1 + 2 * 3;    // 7

            // + operator evaluated first
            int sum2 = (1 + 2) * 3;  // 9

Operands, however, are evaluated from left-right, regardless of the order of evaluation of the operators. For example, in the code below, the sub-expression Return2() * Return3() is evaluated before adding Return1().  But console output shows us that Return1() was executed first.

        static int Return1()
        {
            Console.WriteLine("1");
            return 1;
        }

        static int Return2()
        {
            Console.WriteLine("2");
            return 2;
        }

        static int Return3()
        {
            Console.WriteLine("3");
            return 3;
        }

        static void Main(string[] args)
        {
            // * operator evaluated before +
            int sum1 = Return1() + Return2() * Return3();    // 7
        }

1075-001

#1,074 – Use Parentheses in Expressions to Make Code More Readable

In the absence of parentheses within an expression, operator precedence and associativity dictate how the expression will be evaluated. Technically, you only need parentheses in the expression if you want the expression to be evaluated differently, relative to the precedence and associativity rules.

If your expression doesn’t require parentheses in order to evaluate correctly, it’s often still a good idea to include them.  The parentheses will typically improve the readability of the expression because they make the evaluation order more clear.

            // This is tough to read
            int i5 = 1 + 10 / 5 * 2 - 12 / 4 + 24 % 5 / 2 * 4;

            // This is a bit better
            i5 = 1 + ((10 / 5) * 2) - (12 / 4) + (((24 % 5) / 2) * 4);

#1,073 – Arithmetic Binary Operators are Left-Associative

All arithmetic binary operators (+, -, *, /, %) are left-associative.  This means that when there are multiple operators having the same precedence, the expression is evaluated from left to right.  Below are some examples.

            // Multiplicative, left-right

            // (10 / 5) * 2 = 4
            // [result would be 1 if right-associative]
            int i = 10 / 5 * 2;

            // (40 % 12) * 2 = 8
            // [result would be 16 if right-associative]
            int i2 = 40 % 12 * 2;

            // Additive, left-right

            // (4 - 3) + 5 = 6
            // [result would be -4 if right-associative]
            int i3 = 4 - 3 + 5;

Note that the multiplicative operators (*, /, %) have a higher precedence than the additive (+, -).  This means that if there are no parentheses, the multiplicative operators are evalated before the arithmetic.

            // 1 + (2 * 3) = 7
            int i4 = 1 + 2 * 3;

            // Equivalent to 1 + ((10 / 5) * 2) = 5
            int i5 = 1 + 10 / 5 * 2;

#1,072 – How the Unary Minus Operator Can Fail

There are cases when applying a unary operator to an operand results in a value that does not fit into the data type of the operand. Consider the int type, whose range is -2,147,483,648 to 2,147,483,647.  If we assign the minimum value (largest negative number) and try to negate it with the unary operator, the negation will fail.

By default, arithmetic operations work in an unchecked context.  In this case, the unary operator does nothing, just returning the value of the original operand.

            // int range is -2,147,483,648 to 2,147,483,647
            int n = int.MinValue;
            n = -n;  // n unchanged

If we do the same operation in a checked context, we’ll get an overflow exception.

1072-001

 

Note that a cast to a “larger” data type fails if it’s done outside of the unary operator.  But casting before applying the operator does work.

            long l = (long)-n;  // Still fails

            long l2 = -(long)n;  // This works

 

Follow

Get every new post delivered to your Inbox.

Join 376 other followers