Bitwise operations are operations that operate on the individual bits in a binary representation of a number.
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.
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.
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.
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.
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)
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)
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.
byte a = 2; byte b = 4; byte c = (byte) (a + b); Console.WriteLine(c); //Outputs 6
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.
NaN represents a value that is not a number and can be accessed in the following manner:
The negative infinity constant is the equivalent to dividing a negative number by 0.
The positive infinity constant is the equivalent to dividing a positive number by 0.
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.
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
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.
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 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");