c++ - 功能用途不明,它有什么作用?
问题描述
如果输入是复数a+bi
并且有人可以帮我理解这个函数在做什么real = a, imag = b
我不知道它可能在计算什么,但也许我遗漏了一些明显的东西?
double function(double real, double imag)
{
double y;
double a;
double b;
a = fabs(real);
b = fabs(imag);
if (a < b)
{
a /= b;
y = b * sqrt(a * a + 1.0);
}
else if (a > b)
{
b /= a;
y = a * sqrt(b * b + 1.0);
}
else if (b == NAN)
{
y = b;
}
else
{
y = a * sqrt(2);
}
return y;
}
解决方案
该代码是计算传递给它的复数的大小(绝对值)而不引起不必要的溢出的有缺陷的尝试。
考虑复数a + b i,其中a和b是分配给函数前几行的值a
。b
它的大小是√( a 2 + b 2 )。但是,如果a或b很大,则浮点计算a*a
可能会溢出浮点格式的有限范围并产生无穷大 (∞),即使幅度在该范围内。举个简单的例子,设a为 2 1000,b为 0。那么大小为 √(2 2000 +0) = 2 1000,但计算sqrt(a*a + b*b)
产生无穷大。(由于a*a
溢出并产生了∞,其余的计算也产生了∞。)
该代码试图通过将a和b中较小的一个除以较大的一个并使用数学上等效但不会溢出的计算来解决这个问题。例如,如果a < b
为真,则执行:
a /= b;
y = b * sqrt(a * a + 1.0);
然后a /= b
产生一个小于 1 的值,因此最后一个之前的所有计算都安全地在浮点有限范围内:a * a
小于 1、a * a + 1.0
小于 2 和sqrt(a * a + 1.0)
小于 1.42。当我们乘以 时b
,最终结果可能会溢出到 ∞。发生这种情况的原因有两个:a + b i 的大小可能超出浮点有限范围,因此溢出是正确的。或者先前计算中的舍入可能导致sqrt(a * a + 1.0)
比数学结果略大,并且b * sqrt(a * a + 1.0)
当幅度的实际值在范围内时足以导致溢出。由于这不是我们的重点,我不会在这个答案中进一步分析这个案例。
除了那个四舍五入的问题,前两种情况都很好。但是,此代码不正确:
else if (b == NAN)
根据 IEEE-754 2008 5.11 和 IEEE-754 1985 5.7,NaN 不小于、等于或大于任何操作数,包括它自己。它是无序的。这意味着b == NAN
如果使用 IEEE-754,则必须返回 false。C 2018 不要求使用 IEEE-754,但脚注 22(在 5.2.4.2.2 4)表示,如果不支持 IEC 60559:1989(实际上是 IEEE-754),则术语“安静 NaN”和“信号C 标准中的 NaN”旨在应用于具有类似行为的编码。并且 7.12 5 告诉我们NAN
扩展为float
代表安静的 NaN。因此, inb == NAN
应该NAN
表现为 IEEE-754 NaN,因此b == NAN
应该产生 0,表示 false。
因此,这个由 控制的代码else if (b == NAN)
永远不会被执行:
y = b;
相反,执行落到else
,它执行:
y = a * sqrt(2);
如果a
是 NaN,则根据需要,结果是 NaN。但是,如果a
是一个数字并且b
是一个 NaN,那么当期望的结果是一个 NaN 时,这会产生一个数字作为结果。因此,代码被破坏了。
推荐阅读
- python - 我在理解这个 python 代码时遇到了问题
- javascript - 如何在 React 中使用 reduce 和 promise.all 显示多组数据
- laravel - 无效参数异常 - Laravel 单元测试
- mysql - MySQL Join 使用 WHERE 子句返回太多行
- rxjs - 可以确定史诗在 Redux Observable 中何时结束?
- business-process-management - 如何提取特定用户 IBM BPM 的所有任务 ID
- visual-studio - 如何“在 flutterTest 小部件中导入”
- sql - 在相关子查询中查找多个不同的属性
- python - 使用欧几里得距离损失函数的keras
- swift - 即使网点看起来正确,如何解决错误 SIGABRT?