gpt4 book ai didi

c# - 为什么这个循环只有在我不调试的时候才会无限循环?

转载 作者:太空宇宙 更新时间:2023-11-03 22:36:33 25 4
gpt4 key购买 nike

下面的程序进行了一些计算,以计算需要多少项无限收敛和才能超过某个阈值。我理解/怀疑如果速率太大(例如 750,见下文),这样的循环可能不会终止,因为浮点运算导致计算不准确。

但是,下面的循环在 Debug模式下输出 i=514 (microsoft visual studio .net 4.6.1),但在 Release模式下不会终止(“挂起”)。或许更奇怪:如果我在循环内取消注释“if”部分(意在弄清楚发生了什么),那么 Release模式代码突然也输出 i=514

这背后的原因是什么?如何避免在 Release模式下弹出这样的问题? (编辑:我宁愿不在生产代码中添加 if 语句或 break 语句;此代码应尽可能高效。)

 static void Main(string[] args)
{
double rate = 750;
double d = 0.2;
double rnd = d * Math.Exp(rate);
int i = 0;
int j = 0;
double term = 1.0;
do
{
rnd -= term;
term *= rate;
term /= ++i;
//if (j++ > 1000000)
//{
// Console.WriteLine(d + " " + rate + " " + term);
// j = 0;
// Console.ReadLine();
//}
} while (rnd > 0);
Console.WriteLine("i= "+i);//do something with i
Console.ReadLine();
return;
}

最佳答案

执行摘要 即使在调试中,您的代码也有问题 - 即使在循环退出时它也会产生错误的结果。您需要了解浮点运算的限制。

如果您使用调试器单步执行代码,您很快就会发现问题所在。

Math.Exp(rate) 很大。很大。大于 double 可以容纳。因此 rnd 以值 Infinity 开始。

当你到达 rnd -= term 时,它是 Infinity 减去一些数字,它仍然是 Infinity。因此 rnd > 0 始终为真,因为 Infinity 大于零。

这一直持续到 term 也达到 Infinity。那么rnd -= term就变成了Infinity - Infinity,也就是NaN。任何与 NaN 相比的东西都是假的,所以 rnd > 0 突然是假的,你的循环退出。

我不知道为什么这会在 Release模式中发生变化(我无法重现),但是您的浮点运算顺序完全有可能发生了变化。如果您同时处理大数字和小数字,这会对输出产生巨大影响。例如,term *= rate; term/=++i 可能被排序为 term * rate 总是在 Debug 中首先发生,并且乘法在除法发生之前达到 Infinity。在 Release 中,这可能会被重新排序,以便 rate/++i 首先发生,这会阻止你达到 Infinity。因为您开始时遇到的错误是 rnd 始终是 Infinity,所以只有当 term 也是 Infinity 时,您的循环才会中断>.

我怀疑这也可能取决于您的处理器等因素。

编辑:参见This answer由@HansPassant 提供更好的解释。

因此,虽然挂起和不挂起之间的区别可能取决于调试与发布,但您的代码从一开始就无法正常工作。即使在调试中,它也会产生错误的结果!

如果您处理的是大数或小数,则需要注意 double 的限制。 float 是复杂的野兽,有很多微妙的行为。您需要注意这一点,例如,请参阅这篇著名文章:What Every Computer Scientist Should Know About Floating-Point Arithmetic .注意限制,结合大数和小数的问题等。

如果您在接近 float 极限的情况下工作,则需要检验您的假设:例如,确保您没有处理过大或过小的数字。如果您希望求幂小于 Infinity,请对其进行测试。如果您希望循环退出,请添加一个保护条件以确保它在一定次数的迭代后以错误退出。 测试您的代码!确保它在极端情况下的行为正确。

此外,在适当的地方使用大数库。如果可能,重新设计您的算法以使其对计算机更加友好。许多算法的编写方式使数学家可以将它们写进教科书,但对于处理器来说执行起来却不切实际。通常有一些版本做同样的事情,但对计算机更友好。

I would rather not add an if statement or break statement in production code; this code should be as performant as possible.)

不要害怕循环中的单个 if 语句。如果它总是产生相同的结果——例如你的突破永远不会被击中——分支预测器很快就会流行起来,分支几乎没有成本。这是一个带有不可预测分支的循环,您需要小心。

关于c# - 为什么这个循环只有在我不调试的时候才会无限循环?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54824557/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com