Numeric Types Part 2. Bitwise Operators

Numeric Types Part 2. Bitwise Operators

Bitwise Operations

Bitwise operations are operations that operate on the individual bits in a binary representation of a number.

Complement Bitwise Operator

The complement bitwise operator will take the complement of the bits in representation, in other words, all zeros will be changed to ones and all ones will be changed to zeros.

Complement Bitwise Operator

And Bitwise Operator

The and bitwise operator will check to see if a bit exists in matching positions and if they do, this will be carried into the resulting value.

AND bitwise

Or Bitwise Operator

The bitwise OR operator has the same principle as the bitwise And operator apart from the check is to see if either literal have a 1.

For example

OR bitwise

Exclusive Or Bitwise Operator

The exclusive Or bitwise operator will check to see if either the same position in each literal is 1 but NOT both e.g. 1 ^ 1 == False but 1 ^ 0 or 0 ^ 1 == True.

Exclusive OR operation

Shift Left Bitwise Operator

The shift left bitwise operator will take the current bits in a literal and move them a specified number of positions to the left, generally making the number larger, unless you move a 1 into the signed (negative) position.

int a = 0b0000_1111;
int b = a << 2;

Console.WriteLine(b); //Outputs 60 (00111100)

Shift Right Bitwise Operator

The shift left bitwise operator will take the current bits in a literal and move them a specified number of positions to the right, generally making the number smaller.

int a = 0b0011_1100;
int b = a >> 2;

Console.WriteLine(b); //Outputs 15 (00001111)

8-Bit and 16-Bit Integral Types

As mentioned in a previous section, byte/sbyte and short/ushort integral types lack their own arithmetic operators, however, if we try to perform these operations on 8 and 16-bit types we can see normal behaviour.

To achieve this C# implicitly converts these to be larger types when it is required. The result of the implicit conversion will be of the larger data type, therefore in order to receive the original, smaller data type, you would need an explicit cast back to the original type.

For example:

byte a = 2;
byte b = 4;
byte c = (byte) (a + b);

Console.WriteLine(c); //Outputs 6

Special Values for the Float and Double Types

The float and double types have values that represent special constants such as positive and negative infinity, as well as NaN (Not a Number).

Epsilon represents the smallest positive Double value that is significant in a numeric operation and can be accessed in the following manner.

Console.WriteLine(Double.Epsilon);

NaN represents a value that is not a number and can be accessed in the following manner:

Console.WriteLine(Double.NaN);

The negative infinity constant is the equivalent to dividing a negative number by 0.

Console.WriteLine(Double.NegativeInfinity);

The positive infinity constant is the equivalent to dividing a positive number by 0.

Console.WriteLine(Double.PositiveInfinity );

Double Vs. Decimal Type

Despite both being floating-type values, double and decimal have internal differences which set them apart.

The decimal type was created with dealing with money and other financial functions, this is due to it having base-10 as its internal representation.

Base-10 is important for financial calculations as it has a high precision for numbers that can be represented in base 10 where base 2 might not always be able to do this. For more information on this topic please visit the C# documentation: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/floating-point-numeric-types

However, the decimal type is far slower to use as it isn’t native to the processor, has a much smaller range, as well as not having the special values such as Nan, +0, -0, positive infinity, negative infinity etc.

For this reason, it is recommended that double be used for scientific applications.

Testing for Equality on Numeric Types

The two basic operators that text for equality are ” == ” (equals) and ” != ” (does not equal). These two operators are valid for testing value types such as ints, longs, bytes etc. They can be used in the following manner:

int firstInt = 10;
int secondInt = 20;
int thirdInt = 30 - 10;

Console.WriteLine(firstInt == secondInt); //False
Console.WriteLine(firstInt == thirdInt);  //True 

Reference Type Equality

Testing for equality between reference types with these two operators is not valid. The check is made on the reference rather than the value, so if you were to define two objects from the same class with the same value then the ” == ” operator would still return false. We will look into the comparison of reference types in a later entry.

Logical Operators

The logical operators || (OR) and && (AND) check to see if the boolean representation of values matches logical patters. For example:

Console.WriteLine(true  || true);   //True
Console.WriteLine(false || false);  //False
Console.WriteLine(true  || false);  //True
Console.WriteLine(false || true);   //True

Console.WriteLine(true  && true);   //True
Console.WriteLine(false && false);  //False
Console.WriteLine(true  && false);  //False
Console.WriteLine(false && true);   //False

The not operator (!) can be used to negate the outcome of a logical comparison, for example, with the above example.

Console.WriteLine(!(true  || true));   //False
Console.WriteLine(!(false || false));  //True
Console.WriteLine(!(true  || false));  //False
Console.WriteLine(!(false || true));   //False

Console.WriteLine(!(true  && true));   //False
Console.WriteLine(!(false && false));  //True
Console.WriteLine(!(true  && false));  //True
Console.WriteLine(!(false && true));   //True

The Ternary Operator

The ternary operator is a statement which will have two corresponding outcomes depending on if a statement equates to true or false.

The true result is placed ahead of the false result and is separated by a colon.

Console.WriteLine(true ? "This was true" : "This was false");
Console.WriteLine(false ? "This was true" : "This was false");