在C ++中使用不同的IEEE浮点舍入模式

时间:2016-08-29 09:24:45

标签: c++ rounding ieee-754 idiomatic

我有祸了,我必须在GPU和CPU上确保相同的浮点结果。好的,我理解IEEE已经照顾好我并提供了一个很好的标准来坚持几个舍入选项;并且CUDA部分被整理出来(有不同舍入模式的内在函数),所以这只是动机。

但是在主机端C ++代码中 - 如何在特定的舍入模式下执行浮点运算(我的意思是在特定的语句中,而不是在我的翻译单元中)?是否有使用引擎盖组件的包装功能?是否有一组具有不同舍入模式的浮点数代理类?

我也在询问有关翻译单元级别的相同问题。在编译翻译单元时,如何使编译器(gcc / clang / MSVC)默认为某种舍入模式?

2 个答案:

答案 0 :(得分:0)

在@AndreasPapadopoulos的带领下,看起来像there is an official way更改了舍入模式:

int fesetround(int round)
int fegetround()

但有几点需要注意:

  1. 这是C ++ 11,而不是C ++ 98(虽然在实践中你可能只是使用系统的<fenv.h> C99。
  2. 需要通过#pragma
  3. 与编译器进行通信
  4. 这种转换可能很慢。
  5. 我不确定它在实践中的使用范围有多广(以及周围是否有更好的抽象,更常见的用途)。

答案 1 :(得分:0)

一种简单的处理方法是引入一个类,该类在初始化时设置舍入模式,并在超出范围时将其重置为先前的模式,如下所示:

#include <cfenv>

//Simple class to enable directed rounding in floating-point math and to reset
//the rounding mode afterwards, when it goes out of scope
struct SetRoundingMode {
  const int old_rounding_mode;

  SetRoundingMode(const int mode) : old_rounding_mode(fegetround()) {
    if(std::fesetround(mode)!=0){
      throw std::runtime_error("Failed to set directed rounding mode!");
    }
  }

  ~SetRoundingMode(){
    // More recent versions of C++ don't like exceptions in the destructor
    // so you may need a different way of handling issues here.
    if(std::fesetround(old_rounding_mode)!=0){
      throw std::runtime_error("Failed to reset rounding mode to original value!");
    }
  }

  static std::string get_rounding_mode_string() {
    switch (fegetround()) {
      case FE_DOWNWARD:   return "downward";
      case FE_TONEAREST:  return "to-nearest";
      case FE_TOWARDZERO: return "toward-zero";
      case FE_UPWARD:     return "upward";
      default:            return "unknown";
    }
  }
};

然后你可以像这样使用

void foo(){
  const auto srm = SetRoundingMode(FE_UPWARD);
}

请注意,所有舍入模式都列在类中。