Основываясь на комментариях @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 и Машина B используют разные настройки для _controlfp_s с самого начала.Пример приложения покажет значения управляющего слова в первом блоке выходов на компьютере A, отличные от значений на компьютере B. После того, как приложение принудит Денормированный элемент управления к SAVE, выходные данные должны совпадать.Если это так, то, возможно, вы можете просто принудительно заставить денормализованный элемент управления SAVE на компьютере B при запуске приложения.
- На компьютере A и на машине B используются одинаковые настройки для _controlfp_s и вывода примераПриложение абсолютно одинаково на обеих машинах.Если это так, то в вашем приложении должен быть какой-то код (возможно, DirectX, WPF?), Который переворачивает настройки _controlfp_s на компьютере B, но не на компьютере A.
Если вы получилишанс попробовать пример приложения на каждой машине, пожалуйста, обновите комментарии с результатами.Мне интересно посмотреть, что произойдет.