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

后端 未结 4 665
执笔经年
执笔经年 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:14

    Here's a solution that doesn't fit all the criteria, along with analysis for why not. See the accepted answer for a better answer.

    // Define constants from the question.
    static constexpr int64_t kint64min = std::numeric_limits::min();
    static constexpr int64_t kint64max = std::numeric_limits::max();
    
    int64_t SafeCast(double d) {
      // Handle NaN specially.
      if (std::isnan(d)) return 0;
    
      // Handle out of range below.
      if (d <= kint64min) return kint64min;
    
      // Handle out of range above.
      if (d >= kint64max) return kint64max;
    
      // At this point we know that d is in range.
      return d;
    }
    

    I believe this avoids undefined behavior. There is nothing to be wary of with casting integers to doubles in the range checks. Assuming sanity in the way that non-representable integers are converted (in particular that the mapping is monotonic), by the time the range checks are past, we can be sure that d is in [-2^63, 2^63), as required for the implicit cast at the end of the function.

    I'm also confident that this clamps out of range values correctly.

    The issue is criteria #2 from the update to my question. Consider an implementation where kint64max is not representable as a double, but kint64max - 1 is. Further, assume that this is an implementation where casting kint64max to a double yields the next lower representable value, i.e. kint64max - 1. Let d be 2^63 - 2 (i.e. kint64max - 1). Then SafeCast(d) is kint64max, because the range check converts kint64max to a double, yielding a value equal to d. But static_cast(d) is kint64max - 1.

    Try as I might, I can't find a way to resolve this. Nor can I even write a unit test that checks my criteria, without the unit test executing undefined behavior. I feel like there is a deeper lesson to be learned here—something about the impossibility of detecting whether an action in a system will cause undefined behavior from inside the system itself, without causing undefined behavior.

提交回复
热议问题