C# JIT优化问题 出身背景
因此,我在做一个简单的C#不公平刽子手游戏(根据我向玩家提供的信息,有一个有效解决方案的单词列表(例如单词长度),随着时间的推移,这些单词会逐渐减少,玩家只有在只有一个可能的解决方案并且他们猜到了所有字母的情况下才会赢)。C# JIT优化问题 出身背景,c#,floating-point,compiler-optimization,jit,C#,Floating Point,Compiler Optimization,Jit,因此,我在做一个简单的C#不公平刽子手游戏(根据我向玩家提供的信息,有一个有效解决方案的单词列表(例如单词长度),随着时间的推移,这些单词会逐渐减少,玩家只有在只有一个可能的解决方案并且他们猜到了所有字母的情况下才会赢)。 无论如何,需要知道的重要一点是,我有一行代码如下所示: availableWords.RemoveAll(word=>AmountInCommon(word)!=lowestInCommon) (我从可用文字中删除所有与用户猜到的文字有更多共同字母的文字,而不是可用文字中共同
无论如何,需要知道的重要一点是,我有一行代码如下所示:
availableWords.RemoveAll(word=>AmountInCommon(word)!=lowestInCommon)代码>
(我从可用文字中删除所有与用户猜到的文字有更多共同字母的文字,而不是可用文字中共同字母最少的文字)
问题是,此代码仅在关闭编译器优化时有效。
演示问题的代码
我假设这与浮点数的工作方式有关,但让我困惑的是,为什么打开和关闭编译器优化会影响它。我猜我应该做的就是把浮点值四舍五入到精度的3位左右,因为这就是我真正需要的。为什么会发生这种情况?我的解决方案是最佳实践吗
编辑:
我意识到在这种情况下,由于单词长度没有改变,我可以让AmountInCommon
返回表示共有多少个字母的整数,而不是表示共有字母与单词长度之比的浮点。不过,你的答案仍然很有用。短篇小说
不要比较浮点值是否相等
程序员在说了这句话后通常做的第一件事是建议使用ε。然而,这是错误的<代码>浮点。Epsilon
是大于零的最小可能浮点,但是(更重要的是)它并不意味着它是任意两个浮点之间的最小差值。比较浮动时,更好的做法是选择一个合理的值,以确定两个浮动是否“足够接近”以相等
代码的问题是,首先要对数字进行四舍五入,这可能包括一个显著的差异,以这种方式或另一种方式进行四舍五入
长话短说
根据ECMA规范
9.3.7浮点类型
C#支持两种浮点类型:float和double。浮子和
双类型使用32位单精度和
64位双精度IEC 60559格式
浮点运算的执行精度可能高于
操作的结果类型。[示例:一些硬件
体系结构支持“扩展”或“长双精度”浮点
类型的范围和精度大于双精度类型,以及
使用此更高版本隐式执行所有浮点操作
精密型
更多信息
IEC 60559标准没有为扩展浮点格式的参数指定精确值。它只指定最小值。由于8087,英特尔处理器使用的扩展格式长度为80位,指数为15位,有效位为64位。与其他格式不同,扩展格式为有效位的前导位,用于实现某些处理器优化等
示例
编译器
现在是编译器。编译器和jitter将在CPU上执行代码之前,根据设置优化的方式(以及其他事项),尝试在不同点对代码进行优化
这些优化将影响内存中存储的内容、如何存储以及寄存器和重新组织中设置的内容,以及许多其他可能发生或不发生的微妙指令(取决于一系列因素)。这些优化中的每一个都有可能以非确定性方式改变浮点计算的结果。这正是您的问题所在
总之
当涉及到浮点运算时,永远不要依赖于浮点等式。此外,不要尝试并确定地依赖于来自编译器、抖动、Cpu、体系结构、平台、版本或应用程序位的结果
比较预期范围内的浮点类型(对您而言)。短篇小说 不要比较浮点值是否相等 程序员在说了这句话后通常做的第一件事是建议使用ε。但是这是错误的。
float。ε是可能大于零的最小浮点(更重要的是)这并不意味着它是任意两个浮点数之间的最小差值。在比较浮点数时,更好的做法是选择一个合理的值来确定两个浮点数是否“足够接近”相等
代码的问题是,首先要对数字进行四舍五入,这可能包括一个显著的差异,以这种方式或另一种方式进行四舍五入
长话短说
根据ECMA规范
9.3.7浮点类型
C#支持两种浮点类型:float和double
双类型使用32位单精度和
64位双精度IEC 60559格式
浮点运算的执行精度可能高于
操作的结果类型。[示例:某些硬件]
体系结构支持“扩展”或“长双精度”浮点
类型的范围和精度大于双精度类型,以及
使用此更高版本隐式执行所有浮点操作
精密型
更多信息
IEC 60559标准没有为扩展浮点格式的参数指定精确值。它只指定最小值。ext
class Program {
static void Main() {
float randomPowerOf2 = (float)Math.Pow(2, new Random().Next(-4, 5));
float foo1 = Foo(3f); // some non power of 2
float foo2 = Foo(randomPowerOf2);
float baz = Baz();
// prints false with compiler optimizations; true without compiler optimizations
Console.WriteLine(Math.Round(Foo(3f), 2) == Math.Round(foo1, 2));
// prints true with and without compiler optimizations
Console.WriteLine(Math.Round(Foo(3f), 2) == Math.Round(foo1, 2));
// prints true with and without compiler optimizations
Console.WriteLine(Foo(randomPowerOf2) == foo2);
// prints true with and without compiler optimizations
Console.WriteLine(Baz() == baz);
Console.ReadLine();
}
static float Foo(float divisor) {
return 1 / divisor;
}
static float Baz() {
return 1 / 3f;
}
}