我必须调试一些旧代码。一个问题是识别并消除所有浮点除法。 它们并不多,但我最常发现的是:
dA = bB / cos(x);
和罪恶和晒黑。 x
通常是以度为单位的坐标,因此tan
,cos
和sin
很容易为零。该代码已经存在并运行了数十年,并且由于它是安全关键的,因此它已经过全面的测试。
edit1: x
是double
,通常是在从UTM转换后生成的。度数值在-180.0到+180.0范围内。
编辑2:当然是以弧度为单位,它会被转换。它可能仍然是零,但
所以我的问题是,它为什么永远不会崩溃?我错过了什么吗?
编辑3 我跟进了所有评论,并为我找到了正确的答案。这是一个令人讨厌的例外。我们得到一个例外的原因是我们建立了别人的代码(由客户提供),这是非常糟糕的。他们在(/ fp:except)上切换了浮点异常,如果你需要它当然很好。然而,他们没有再次关闭它,所以我们的代码开始抛出前所未有的异常。这是通过dll而不是exe调用新东西来修补的,但让我的行动让人们再次开心。 我正在查看每个部门的代码,看它是否可以被零除,如果这是一件坏事,并且因为我现在知道三角运算对NaN很满意,这也不会太过分坏。我还是要检查一下。哎哟。
答案 0 :(得分:3)
代码运行是因为cos(x)
的零位于pi/2 + k*pi; k € Z
。但pi
无法准确表示。因此不可能完全为零。
答案 1 :(得分:2)
double
有special value for infinity。 0
除以此特殊值。
请注意,x
应为弧度,而不是度数。
刚刚在一些C ++编译器中测试过:
#include <iostream>
#define USE_MATH_DEFINES
#include <cmath>
int main()
{
std::cout << (1 / std::cos(M_PI_2)) << "\n";
std::cout << (1 / std::sin(0)) << "\n";
}
得到了:
1.63312e+16
inf
所以1 / cos(M_PI_2)
可以是非常大的数字,甚至无穷大。我想这对于日常的数值计算是可以的。
答案 2 :(得分:2)
旁注:函数余弦在90°度(或π/ 2弧度)时为零。从空间的角度来看,这对应于......垂直的东西。假设你的代码永远不会处理垂直的东西,很有可能它会在没有崩溃的情况下继续运行一段时间。请分享有关x
的更多详细信息,其中应为弧度,而非度数。
答案 3 :(得分:2)
浮点数学旨在在出现错误时继续前进。将非零值除以0会产生无穷大;将0除以0会产生NaN。这两个值都可以用于后续计算,并且它们或多或少地传播。如果最终结果有无穷大或NaN,告诉你一路上出了问题,你必须找出导致它的原因:坏数据或坏代码。
这是否安全&#34;取决于你的意思。如果你不知道自己在做什么,那么没有什么是安全的。如果您了解错误如何在浮点代码中传播,您可以使用它并生成可靠,可靠的结果。 (对不起,如果这有点超过顶部;我真的,真的讨厌单词&#34; safe&#34;当它应用于软件时,因为它&#39;通常是空的)
答案 4 :(得分:1)
广泛的,一般的术语,因为只有零余弦的实数值是无理的(即pi/2
的奇数倍),而浮点值不能表示无理实际值,任何浮点值都不可能存在零余弦的存在。
在非关键代码中,这种论点可能就足够了。它(取决于系统要求)在高临界性(例如安全关键)代码中是不可接受的。
同样地,像“它已经在所有地方进行过测试”之类的声明也不一定是可接受的 - 如果你想使用这样的论证(在某些安全标准中更正式地称为服务历史论证)那么onus是你的理由。
我希望,如果系统以前被认为可以接受用于安全相关的应用程序,就像你说的那样,会对像dA = bB / cos(x)
之类的语句可能产生错误结果的情况进行一些分析(例如浮点溢出,除零,......)。该分析将取决于cos()
函数的实现方式 - 例如它在您选择的标准库中使用的算法。然后,在给定可能的程序输入范围的情况下,分析将考虑任何错误结果对这种错误结果的整体系统行为(不仅仅是程序)以及发生的可能性的影响。从本质上讲,这意味着完成的风险评估 - 如果风险可接受的低(在系统环境中),那么它将被接受。
这样说:想象一下这段代码是控制电梯的固件的一部分。您将更新此代码,重新编译它,并将更新的固件安装到多个电梯中。现在想象一下dA
的值是否会影响电梯行进的速度 - 因此危险在于电梯以足以杀死乘客的速度撞击地面。您经常关心的一些家庭成员经常使用您正在更新的其中一台电梯。你会接受“它已经在各地进行过测试”的论点吗?或者你想要一个更强大的论点?