Joh*_*old 32 .net c# math epsilon
我发现跑步了
Math.Log10(double.Epsilon)
Run Code Online (Sandbox Code Playgroud)
将-324在机器A上返回,但将-Infinity在机器B上返回.
他们最初的表现方式与回归相同-324.
两台机器都使用相同的操作系统(WinXP SP3)和.NET版本(3.5 SP1).机器B上可能有Windows更新,但是不知道发生了什么变化.
什么可以解释行为上的差异?
评论中讨论的更多细节:
Math.Log10(double.Epsilon)在机器B上打印一个简单的控制台应用程序-324,而不是-Infinity0x9001F(读取_controlfp()).更新:最后一点(FPU控制字)不再成立:使用较新版本的_controlfp()显示不同的控制字,这解释了不一致的行为.(有关详细信息,请参阅下面的rsbarro答案.)
rsb*_*rro 22
基于由@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();
}
}
}
Run Code Online (Sandbox Code Playgroud)
有几点需要注意.首先,我必须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
Run Code Online (Sandbox Code Playgroud)
要确定机器A和机器B的运行情况,您可以使用上面的示例应用程序并在每台机器上运行它.我想你会发现:
如果您有机会在每台计算机上试用示例应用程序,请使用结果更新注释.我很想知道会发生什么.
可能会将dll加载到与x87浮点标志混淆的进程中.DirectX/OpenGL相关库因此而臭名昭着.
在jitted代码中也可能存在差异(浮点不需要在.net中以特定方式运行),但由于您使用相同的.net和OS版本,因此不太可能.
在.net常量中加入到调用代码中,因此它们之间应该没有区别double.Epsilons.
| 归档时间: |
|
| 查看次数: |
2139 次 |
| 最近记录: |