#1,100 – Increment and Decrement Operators Are Not Thread-Safe

If you have multiple threads increment or decrementing a variable using the increment or decrement operators, you may not get the desired result.  These operators are not thread-safe.

Imagine two threads that increment a variable.  If they do the operation serially, the variable ends up correctly incremented twice.  If the two threads read the value at the same time, however, they may both end up writing the same value back to the variable.  The variable ends up incremented just once.

You can do a safe increment operation using System.Threading.Threadlocked.Increment.

        private static int counter1 = 0;
        private static int counter2 = 0;

        static void Main(string[] args)
        {
            int maxExclusive = 1001;

            Parallel.For(1, maxExclusive, n =>
            {
                // Do some work here.
                Thread.Sleep(4);
                counter1++;
                Interlocked.Increment(ref counter2);
            });

            Console.WriteLine(
                "Ran {0} iterations, counter 1 = {1}, counter 2 = {2}",
                maxExclusive - 1, counter1, counter2);

            Console.ReadLine();
        }

Note below that Counter #1 is not incremented properly.
1100-001

Advertisements

#1,099 – Overloading the Increment Operator

You can overload the increment (++) operator in a class, providing custom increment functionality for an object.

The example below shows an overloaded increment operator defined in a Dog class.  The effect is to add 1 to the age of the dog.  We are careful to return a reference to the object that was passed in so that no other data in the instance changes.

    public class Dog 
    {
        // Increment a Dog
        public static Dog operator ++(Dog d)
        {
            d.Age++;
            return d;
        }

        public string Name { get; set; }
        public int Age { get; set; }
        public string Nickname { get; set; }

        public Dog(string name, int age)
        {
            Name = name;
            Age = age;
            Nickname = "?";
        }

        public override string ToString()
        {
            return string.Format("{0}, Age {1}, Nickname [{2}]", Name, Age, Nickname);
        }
    }

We can use the operator as follows:

            Dog kirby = new Dog("Kirby", 10);
            kirby.Nickname = "Ball Chaser";
            Console.WriteLine(kirby);

            kirby++;
            Console.WriteLine(kirby);

1099-001

#1,098 – Floating Point Overflow

When you perform an arithmetic operation on a floating point value and the result has a magnitude that is too large to be represented by the floating point data type, an overflow condition occurs.  This is equivalent to trying to store a value whose exponent is larger than the maximum allowed exponent value.

If the result of the calculation is greater than the maximum representable positive floating point value, the result of the calculation is +Infinity.  If the result of the calculation is less than the largest representable negative floating point value, the result of the calculation is -Infinity.

            // Create a big floating point number
            float bigNumber = float.MaxValue;
            Console.WriteLine(bigNumber);  // 3.402823E+38

            // Adding small number doesn't change large number
            float newBig = bigNumber + 1.0f;
            Console.WriteLine(newBig);     // 3.402823E+38

            // But doubling the original number leads to overflow
            float reallyBig = bigNumber * 2.0f;
            Console.WriteLine(reallyBig);

1098-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

#1,095 – How Floating Point Infinity Values Are Stored

.NET floating point types can represent both positive and negative infinity floating point values.  They are stored as 32-bit floating point values as follows:

  • +Infinity : sign bit 0, mantissa 0 (23 bits), exponent FF (hex) (8 bits)
  • -Infinity : sign bit 1, mantissa 0 (23 bits), exponent FF (hex) (8 bits)

Positive infinity is therefore stored as 7F800000 (hex) and negative zero as FF800000 (hex).  We can verify this by looking at these values in memory from within Visual Studio.

Assuming the following code:

            float posInfinity = float.PositiveInfinity;
            float negInfinity = float.NegativeInfinity;

We can now look at these values in memory.

Positive infinity is 7F800000 (stored little-endian).

1095-001

Negative infinity is FF800000 (stored little-endian).

1095-002

#1,094 – Positive and Negative Infinity

Because .NET uses the IEEE 754 standard to represent floating point numbers, it allows representing both positive and negative infinity.  The two values of infinity are special values that can be represented by the 32-bit float type or the 64-bit double.  Mathematically, infinity is a concept that represents a value greater than any other real number (positive infinity), or smaller than any other real number (negative infinity).

You can specify a value of infinity using the PositiveInfinity and NegativeInfinity static properties of the float class.

            float posInfinity = float.PositiveInfinity;
            float negInfinity = float.NegativeInfinity;

In Visual Studio, the debugger will list these values as Infinity or -Infinity.

1094-001

You can also generate a positive or negative infinity value as a result of dividing a positive or negative number by zero.  Doing these calculations does not result in an exception.

            float posInfinity = 1.0f / 0;
            float negInfinity = -1.0f / 0;