【问题标题】:Simplify fractions at compile time to avoid division by zero在编译时简化分数以避免被零除
【发布时间】:2016-04-27 09:21:20
【问题描述】:

有没有办法在评估之前简化分数以避免被零除?

例子:

double x1 = 2;
double x2 = 2;
double x3 = 2;

double r1 = (x1-x2)/(x1-x2-x3);
cout << r1 << endl;
x3 = 0;
r1 = (x1-x2)/(x1-x2-x3);
cout << r1 << endl;

将返回:

-0
-nan

是否可以让编译器在编译时简化第二个表达式以避免被零除?特别是,如果x3 等于0,我希望(x1-x2)/(x1-x2-x3) 被简化为1。

在我的代码中,x1x2x3 因子被替换为函数。

【问题讨论】:

  • 所以你想得到像Division by 0这样的编译错误?
  • 即使在编译时,除以零也会产生 NaN 或 Inf,这有什么帮助?
  • 怎么会返回1?您正在将零除以零...
  • 抱歉,这完全是错误的。从代数中你应该知道,等式两边都乘以 0,所有信息都会丢失!通过抵消实际上为零的项来“简化”分数也是如此。这是不允许的!
  • 您在这里试图解决的真正问题是什么?似乎您已经选择了一个解决方案,它可能不是最好的;)

标签: c++ numeric fractions


【解决方案1】:

我已经在 cmets 中声明了以下内容(这可能听起来有点粗鲁,但这只是因为我认为消除这种误解非常重要):您无法通过简单地重新排列分数或进行取消来避免除以零。要了解这是为什么,请考虑以下示例:

 x/y

这不一样

 (0*x) / (0*y)

因为这会显着改变分数的值。总体来说

 x/y = (a*x) / (a*y)

为真,仅当a非0。另一种看待这一点的方法是考虑方程和可能的变换。考虑

a = b

这绝对和

不一样
0*a = 0*b

但是

(a = b)  is equivalent to (a*x = b*x) 

仅当 x 不为 0 时才成立。

如果您想避免被零除,唯一的方法是检查除数是否为 0,并以某种方式处理这种情况。您不能通过取消 0 来避免除以零,因为这会改变分数的值。

【讨论】:

  • 感谢您指出这一点(不过我自己应该记得的)。事实上,这导致了我从文献中得到的公式中一个未被发现的特殊情况。
【解决方案2】:

没有。特别是如果x1,x2,x3的值是通过函数调用得到的,编译器怎么知道x3是0,所以表达式简化为(x1-x2)/(x1-x2)?

但是,您可以在代码中明确地进行所需的测试,以检查是否有一种方法可以在进行除法之前简化表达式。

【讨论】:

  • 将函数定义为 constexpr 以便在编译时提供此信息是否有帮助?
  • 我可以帮忙。但是为了保证编译时评估,您需要将值分配给编译时常量。见stackoverflow.com/questions/20682915/…。此外,我不确定编译器本身是否不会因除以零错误而停止编译。
【解决方案3】:

正如 dim 在他的回答中提到的,为什么不直接实施代码检查呢?这将是一个简单的 if 语句,并且与运行时定义的变量兼容。

太糟糕了,我还不能发表评论,因为在你的问题下面有一个关于除以零的有趣且主要是错误的讨论。当然,将 0/0 设置为 1 是有意义的。更重要的是,您向我们展示的 x3 -> x1 - x2 表达式的极限是 1(或 -1,取决于您来自哪里)。这意味着在特殊情况下,其中许多是实际用例,您可能希望将此特定的 n/A 设置为 1,以便可以继续进行计算而不会抱怨不重要的(在某些情况下是非物理的)无穷大。显然,我要让您自己找到该限制在数学上(或至少在物理上)的正确用途,因为您并没有确切地告诉我们您想做什么。

【讨论】:

    猜你喜欢
    • 2013-07-14
    • 1970-01-01
    • 2011-02-21
    • 2019-02-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多