如何在不需要 if 测试的情况下让 C++ Signaling_nan() 抛出异常?

问题描述

如果在设置之前使用了其值将在初始化后设置的变量,我正在尝试使用 Signaling_nan() 自动抛出异常。

我尝试通过 fenv.h 使用 floating point exception functionalities,但是该解决方案需要对 FE_INVALID 进行 if 测试,我想避免这种情况。

我在下面包含了一个简化的示例代码。在代码中,我有一个带有成员 double x 的类 A,我在构造函数中将其初始化为 Signaling_nan()。该类还包括一个函数divide_by_x,它对类成员x传入的参数进行除法。我创建了一个对象 A a1,并调用 a1.divide_by_x(10.0)。我希望程序抛出异常,但它继续执行并返回 nan 作为divide_by_x 函数调用的值。

在示例代码中,我在程序结束时包含了对 FE_INVALID 浮点异常的 if 测试,如果 x 使用 quiet_nan() [Not invalid] 与signaling_nan() [FE_INVALID]。这表明signaling_nan()确实产生了浮点异常,但程序继续执行。

#include<iostream>
#include <utility>
#include <limits>
#include <float.h>
#include <signal.h>
#include <fenv.h>

using namespace std;

class A
{
public:

  // Constructor
  A():x(std::numeric_limits<double>::signaling_NaN()){}
  //A():x(std::numeric_limits<double>::quiet_NaN()){}

  // Set x
  void set_x(double x_value) { x = x_value; }

  // Divide number by x
  double divide_by_x(double other_number)
  {
    double result = other_number/x;

    return result;
  }
  
  // Member
  double x;
};
  
int main(void)
{
  feclearexcept(FE_ALL_EXCEPT);
  
  A a1;

  double before_setting_x;

  before_setting_x = a1.divide_by_x(10.0);

  if (fetestexcept(FE_INVALID))
        printf("FE_INVALID\n");
    else
        printf("Not invalid\n");
  
 return 0;
}

使用signaling_nan()初始化的x输出

FE_INVALID

使用 quiet_nan() 初始化的 x 输出

无效

解决方法

所以,有很多方法可以解决这个问题:

自定义类,用于检查操作中的 FPE

最简单的方法是编写一个包装类,它检查浮点异常,然后在以后永远不必处理它,所有数字运算都是内置的。

#include <exception>
#include <fenv.h>

class FloatingPointException: public std::exception {
    const char* what() const throw() {
        return "Exception raised in floating-point operation.";
    }
};

class F64 {
public:
    F64(double f): m_f(f) {}
    F64(const F64 &other): m_f(other.m_f) {} 
    F64(F64 &&other): m_f(other.m_f) {}
    F64& operator=(const F64& other) { m_f = other.m_f; return *this; }
    F64& operator=(F64&& other) { m_f = other.m_f; return *this; }

    operator double() const { return m_f; }

    // This is where we use fenv.
    F64 operator*(F64 other) const {
        feclearexcept(FE_ALL_EXCEPT);
        auto result = m_f * other.m_f;
        if (fetestexcept(FE_INVALID)) {
            throw FloatingPointException();
        }
        return F64(result);
    }

    F64& operator*=(F64 other) {
        operator=(operator*(other));
        return *this;
    }

    // This is where we use fenv.
    F64 operator/(F64 other) const {
        feclearexcept(FE_ALL_EXCEPT);
        auto result = m_f / other.m_f;
        if (fetestexcept(FE_INVALID)) {
            throw FloatingPointException();
        }
        return F64(result);
    }

    F64& operator/=(F64 other) {
        operator=(operator/(other));
        return *this;
    }

    // Implement all other operations.
private:
    double m_f;
};

feenableexcept

另一种方法是在程序开始时使用 feenableexcept

#include <fenv.h>
int main() {
    feenableexcept(FE_INVALID);
    ...
    return 0;
}

如果发生无效的浮点异常,这将产生一个 SIGFPE。您还可以使用 GCC 扩展,以便在 main 之前自动发生。

#define _GNU_SOURCE 1
#include <fenv.h>
static void __attribute__ ((constructor)) trapfpe {
    feenableexcept(FE_INVALID);
}

您也可以将其与信号处理程序一起使用。

定制组装

我强烈建议不要这样做,因为它不是可移植的(即使在 x86 上,因为 x87 FPU 与具有 SSE2 的较新 x86 CPU 会有所不同。但是,您可以尝试使用内联汇编来检查浮动- 点异常,尽管这可能是一个非常糟糕的主意。例如,处理 x87 FPU 状态寄存器的细节here

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...