How do I convert an arbitrary double to an integer while avoiding undefined behavior?

后端 未结 4 672
执笔经年
执笔经年 2021-02-13 16:28

Let\'s say I\'ve got a function that accepts a 64-bit integer, and I want to call it with a double with arbitrary numeric value (i.e. it may be very large in magnit

4条回答
  •  星月不相逢
    2021-02-13 17:00

    It turns out this is simpler to do than I thought. Thanks to Michael O'Reilly for the basic idea of this solution.

    The heart of the matter is figuring out whether the truncated double will be representable as an int64_t. You can do this easily using std::frexp:

    #include 
    #include 
    
    static constexpr int64_t kint64min = std::numeric_limits::min();
    static constexpr int64_t kint64max = std::numeric_limits::max();
    
    int64_t SafeCast(double d) {
      // We must special-case NaN, for which the logic below doesn't work.
      if (std::isnan(d)) {
        return 0;
      }
    
      // Find that exponent exp such that
      //     d == x * 2^exp
      // for some x with abs(x) in [0.5, 1.0). Note that this implies that the
      // magnitude of d is strictly less than 2^exp.
      //
      // If d is infinite, the call to std::frexp is legal but the contents of exp
      // are unspecified.
      int exp;
      std::frexp(d, &exp);
    
      // If the magnitude of d is strictly less than 2^63, the truncated version
      // of d is guaranteed to be representable. The only representable integer
      // for which this is not the case is kint64min, but it is covered by the
      // logic below.
      if (std::isfinite(d) && exp <= 63) {
        return d;
      }
    
      // Handle infinities and finite numbers with magnitude >= 2^63.
      return std::signbit(d) ? kint64min : kint64max;
    }
    

提交回复
热议问题