Alex Rivera | Logout

.NET Math.Log10() behaves differently on different machines

Asked 2011-08-08T21:55:01.797
32

I found that running

Math.Log10(double.Epsilon) 

will return about -324 on machine A, but will return -Infinity on machine B.

They originally behaved the same way by returning -324.

Both machines started out using the same OS (WinXP SP3) and .NET version (3.5 SP1). There may have been Windows updates on machine B, but otherwise no changes are known to have happened.

What could explain the difference in behavior?

More details from discussions in comments:

  • Machine A CPU is a 32-bit Intel Core Duo T2500 2 GHz
  • Machine B CPU is a 32-bit Intel P4 2.4 GHz
  • Results collected from code running in a large application using several 3rd party components. However, same .exe and component versions are running on both machines.
  • Printing Math.Log10(double.Epsilon) in a simple console application on machine B prints -324, NOT -Infinity
  • The FPU control word on both machines is always 0x9001F (read with _controlfp()).

UPDATE: The last point (FPU control word) is no longer true: Using a newer version of _controlfp() revealed different control words, which explains the inconsistent behavior. (See rsbarro's answer below for details.)

Edit
Report

1 Answer

22

Based on the comments by @CodeInChaos and @Alexandre C, I was able to throw together some code to reproduce the issue on my PC (Win7 x64, .NET 4.0). It appears this issue is due to the denormal control that can be set using _controlfp_s. The value of double.Epsilon is the same in both cases, but the way it is evaluated changes when the denormal control is switched from SAVE to FLUSH.

Here is the sample code:

using System;
using System.Runtime.InteropServices;

namespace fpuconsole
{
    class Program
    {
        [DllImport("msvcrt.dll", EntryPoint = "_controlfp_s",
            CallingConvention = CallingConvention.Cdecl)]
        public static extern int ControlFPS(IntPtr currentControl, 
            uint newControl, uint mask);

        public const int MCW_DN= 0x03000000;
        public const int _DN_SAVE = 0x00000000;
        public const int _DN_FLUSH = 0x01000000;

        static void PrintLog10()
        {
            //Display original values
            Console.WriteLine("_controlfp_s Denormal Control untouched");
            Console.WriteLine("\tCurrent _controlfp_s control word: 0x{0:X8}", 
                GetCurrentControlWord());
            Console.WriteLine("\tdouble.Epsilon = {0}", double.Epsilon);
            Console.WriteLine("\tMath.Log10(double.Epsilon) = {0}",
                Math.Log10(double.Epsilon));
            Console.WriteLine("");

            //Set Denormal to Save, calculate Math.Log10(double.Epsilon)
            var controlWord = new UIntPtr();
            var err = ControlFPS(controlWord, _DN_SAVE, MCW_DN);
            if (err != 0)
            {
                Console.WriteLine("Error setting _controlfp_s: {0}", err);
                return;
            }
            Console.WriteLine("_controlfp_s Denormal Control set to SAVE");
            Console.WriteLine("\tCurrent _controlfp_s control word: 0x{0:X8}",
answered 2011-08-09T15:05:13.927

Your Answer