Ответ 1
Основываясь на комментариях @CodeInChaos и @Alexandre C, мне удалось собрать код, чтобы воспроизвести проблему на моем ПК (Win7 x64,.NET 4.0). Похоже, эта проблема связана с денормальным контролем, который можно установить с помощью _ controlfp_s. Значение double.Epsilon одинаково в обоих случаях, но способ, которым он оценивается, изменяется, когда денормальное управление переключается с SAVE на FLUSH.
Вот пример кода:
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}",
GetCurrentControlWord());
Console.WriteLine("\tdouble.Epsilon = {0}", double.Epsilon);
Console.WriteLine("\tMath.Log10(double.Epsilon) = {0}",
Math.Log10(double.Epsilon));
Console.WriteLine("");
//Set Denormal to Flush, calculate Math.Log10(double.Epsilon)
err = ControlFPS(controlWord, _DN_FLUSH, MCW_DN);
if (err != 0)
{
Console.WriteLine("Error setting _controlfp_s: {0}", err);
return;
}
Console.WriteLine("_controlfp_s Denormal Control set to FLUSH");
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("");
}
static int GetCurrentControlWord()
{
unsafe
{
var controlWord = 0;
var controlWordPtr = &controlWord;
ControlFPS((IntPtr)controlWordPtr, 0, 0);
return controlWord;
}
}
static void Main(string[] args)
{
PrintLog10();
}
}
}
Несколько вещей, чтобы отметить. Во-первых, я должен был указать CallingConvention = CallingConvention.Cdecl
в объявлении ControlFPS
, чтобы избежать исключения неуравновешенного стека во время отладки. Во-вторых, мне пришлось прибегнуть к небезопасному коду, чтобы получить значение управляющего слова в GetCurrentControlWord()
. Если кто-нибудь знает, как лучше написать этот метод, пожалуйста, дайте мне знать.
Вот результат:
_controlfp_s Denormal Control untouched
Current _controlfp_s control word: 0x0009001F
double.Epsilon = 4.94065645841247E-324
Math.Log10(double.Epsilon) = -323.306215343116
_controlfp_s Denormal Control set to SAVE
Current _controlfp_s control word: 0x0009001F
double.Epsilon = 4.94065645841247E-324
Math.Log10(double.Epsilon) = -323.306215343116
_controlfp_s Denormal Control set to FLUSH
Current _controlfp_s control word: 0x0109001F
double.Epsilon = 4.94065645841247E-324
Math.Log10(double.Epsilon) = -Infinity
Чтобы определить, что происходит с машиной A и машиной B, вы можете взять пример приложения выше и запустить его на каждой машине. Я думаю, вы найдете это:
- Машины A и Machine B с самого начала используют разные настройки для _controlfp_s. Пример приложения отобразит различные значения управляющего слова в первом блоке выходов на машине А, чем на машине В. После того, как приложение заставит Денормальный элемент управления SAVE, результат должен совпадать. Если это так, возможно, вы можете просто заставить денормальный элемент управления СОХРАНИТЬ на машине B, когда ваше приложение запустится.
- Машины A и Machine B используют одни и те же настройки для _controlfp_s, а выход примерного приложения на обеих машинах точно такой же. Если это так, то в вашем приложении должен быть какой-то код (возможно, DirectX, WPF?), Который переворачивает параметры _controlfp_s на машине B, но не на машине A.
Если у вас есть возможность попробовать пример приложения на каждом компьютере, обновите комментарии к результатам. Мне интересно посмотреть, что произойдет.