#1,127 – Where to Find Compiler Warning Numbers

You can use #pragma warning to selectively disable certain warnings that occur when compiling your code.

The #pragma warning line requires knowing the specific warning number, which is not displayed in the list of warnings on the Error List tab.

1127-001

To find the warning number, you need to switch to the Output tab and look at the full text of the warning.  In the example below, we see that the warning about an event that is never used is warning #67.

1127-002

Once you know the warning number, you can use it with a #pragma warning.

#pragma warning disable 67
        public event EventHandler CanBarkChanged;
#pragma warning restore 67

#1,113 – Addition of Integers Using Twos Complement

The primary benefit of using twos complement to represent signed integers is that addition and subtraction work the same, whether the number is positive or negative.

Below is an example, showing how we add the value -2 to the value +5, yielding a result of +3.  (Assuming that both values are stored as 16-bit integers).  The carry bits are shown in the top line.  We discard the final carry bit that results from adding the leftmost two bits.

1113-001

#1,112 – How Integers Are Stored in .NET

Integer values in .NET are stored in memory as described below.  The examples below use a 2-byte short data type as an example.

A value of zero is stored by setting every bit in the storage location to zero.

Positive values are stored directly as their corresponding binary number.

1112-001

The maximum possible integer value has all bits set to 1 except for the leftmost.  So Max = 2^(n-1) – 1, where n is the total number of bits.

Negative integer values are stored using a two’s complement representation, calculated by starting with the bit pattern for the corresponding positive number, negating all of the bits, and then adding 1 to the result.

1112-002

The minimum possible integer value (largest negative value) has only the leftmost bit set to 1.  So Min = -(2^(n-1)), where n is the total number of bits.

 

 

#1,106 – Using the Logical Exclusive OR Operator

You can use the exclusive OR (^) operator to do a bitwise exclusive OR operation.  The operands in this case are integer-based types.

You can also use the exclusive OR operator on boolean operands.  The result of the operation is true if exactly one of the operands (but not both) is true.

  • false ^ false => false
  • false ^ true => true
  • true ^ false => true
  • true ^ true => false
            bool itsThursday = DateTime.Now.DayOfWeek == DayOfWeek.Thursday;
            bool itsMay = DateTime.Now.Month == 5;

            // Wear red shirt in May, wear red shirt every Thursday,
            // but don't wear red shirt on Thursdays in May
            bool wearRedShirt = itsThursday ^ itsMay;

            Console.WriteLine("Thursday:{0}, May:{1}, RedShirt:{2}", itsThursday, itsMay, wearRedShirt);

1106-001

#1,105 – Re-Declaring a Class-Level Variable within a Method

You can re-declare a class-level variable within a method, that is–declare a local variable having the same name as a class-level variable.  Within the scope of the method, the local variable will hide the class-level variable.

    public class MyClass
    {
        public int x = 10;

        public void MethodA()
        {
            double x = 4.2;
            Console.WriteLine(x);
        }
    }

1105-001
You cannot, however, reference a class-level variable before declaring the local variable, since this is interpreted as referencing the local variable before it is defined.

        public void MethodA()
        {
            // ERROR: Can't use local variable before it's declared
            Console.WriteLine(x);

            double x = 4.2;
            Console.WriteLine(x);
        }

You also can’t reference the class-level variable in an outer scope.

        public void MethodA()
        {
            Console.WriteLine(x);

            if (DateTime.Now.DayOfWeek == DayOfWeek.Tuesday)
            {
                // ERROR: Can't declare local variable within this scope
                double x = 4.2;
                Console.WriteLine(x);
            }
        }

#1,104 – Can’t Re-Declare a Variable within a Nested Block

A declaration space is a region of code within which you can’t declare two entities having the same name.  

Defining a class creates a new declaration space, i.e. you can declare variables at the class level.

Defining a method creates a new declaration space, known as a local variable declaration space.  A block of code within that method creates a nested local variable declaration space.

It’s of course an error to declare two variables of the same name within a local variable declaration space.  It’s also an error to re-declare a variable of the same name in a nested local variable declaration space.

        public void MethodA()
        {
            int y = 1;
            string y = "oops";  // ERROR

            int x = 12;
            Console.WriteLine(12);

            if (DateTime.Now.DayOfWeek == DayOfWeek.Thursday)
            {
                // ERROR: Can't re-declare x within nested local variable declaration space
                double x = 4.2;
                Console.WriteLine(x);
            }
        }

#1,103 – A Block Defines Both Scope and Declaration Space

  • Scope is the region of code in which you can refer to a named entity using its unqualified name.
  • Declaration space is a region of code within which you can’t declare two entities having the same name

A block of statements defines both a new scope and a new declaration space.

In the code below, the variable msg is declared within the block of statements following the if statement.

    public class MyClass
    {
        public void AMethod()
        {
            if (DateTime.Now.DayOfWeek == DayOfWeek.Thursday)
            {
                // Can define variables within this block
                string msg = "Hi";
                Console.WriteLine("{0}, it's Thursday. ", msg);
            }

            // ERROR: The name 'msg' does not exist in the current context
            Console.WriteLine(msg);
        }
    }

The block defines a new scope–the variable msg can be referred to by name anywhere within the block (after the declaration).  This block also defines a declaration space–you can only declare one variable named msg within this block.

#1,102 – Scope vs. Declaration Space

The terms scope and declaration space are similar, but slightly different.

  • Scope is the region of code in which you can refer to a named entity using its unqualified name.
  • Declaration space is a region of code within which you can’t declare two entities having the same name

For example:

    public class MyClass
    {
        int count1 = 1;
        int count2 = 2;

        public void AMethod()
        {
            int count2 = 22;

            Console.WriteLine(count1 + ", " + count2);
        }
    }

Then:

  • The class-level declarations of count1 and count2 are in the same scope and in the same declaration space
  • The body of function AMethod()
    • is included in the scope in which class-level fields are defined, i.e. you can reference them using their unqualified name.
    • defines a new declaration space, i.e. we can define a new variable of the same name as the class-level fields
    • defines a new scope, in which we can define variables, nested within outer scope

1102-001

#1,097 – Summary of how Floating Point Numbers Are Stored in .NET

Here’s a complete summary of how 32-bit and 64-bit floating point numbers are represented in .NET, including special values.  For more background, look here and here.

Floating point numbers are stored in .NET according to the IEEE 754 standard:

  • Normalized values (1.bb x 2^bb)
    • Sign bit – positive/negative
    • Mantissa – normalized binary number, does not store leading 1  (23 or 52 bits)
    • Exponent – biased, add 127 (or 1023) to exponent before storing  (8 or 11 bits)
  • Subnormal numbers
    • Sign bit – positive/negative
    • Mantissa – non-normalized, no implied leading 1  (23 or 52 bits)
    • Exponent – 0
  • Positive/negative zero
    • Sign bit – positive/negative
    • Mantissa – 0
    • Exponent – 0
  • Positive/negative infinity
    • Sign bit – positive/negative
    • Mantissa – 0
    • Exponent – FF (hex) or 7FF (hex)
  • NaN
    • Sign bit – not defined (implementation dependent)
    • Mantissa – some non-zero value
    • Exponent – FF (hex) or 7FF (hex)

Ranges for (normalized) numbers represented as 32- and 64-bit floating point numbers:

  • 32-bit float: -3.4 x 10^38 to 3.4 x 10^38
  • 64-bit double: -1.7 x 10^308 to 1.7 x 10^308

#1,096 – Floating Point NaN Values

We’ve seen that floating point numbers can represent approximations of real number values, positive and negative zero values, and positive and negative infinity.

A floating point variable or memory location can also represent a value that is “Not a Number”, normally denoted by the keyword NaN.  A NaN value is a floating point value that is a result of a calculation that leads to a value that is not a real number or a positive or negative infinity value.

NaN values are used when it’s useful to capture the fact that a calculation led to a value that is not a valid numerical result.

Below are some examples of calculations that can lead to NaN values.  Note that we can use float.IsNaN to check for this value.

            float zeroOverZero = 0.0f / 0.0f;
            float zeroTimesInfinity = 0.0f * float.PositiveInfinity;
            float InfinityCalc = float.PositiveInfinity + float.NegativeInfinity;

            double rootNegOne = Math.Sqrt(-1.0);

1096-001