可靠的浮点/整数类型转换溢出检测

pla*_*cel 5 c++ floating-point integer integer-overflow type-conversion

有没有一种安全的方法来可靠地确定整数类型是否T可以存储浮点整数值f( so f == floor(f))而不会发生任何溢出?

请记住,不能保证浮点类型F与 IEC 559 (IEEE 754) 兼容,并且有符号整数溢出是C++ 中的未定义行为。我对根据当前 C++(编写时为 C++17)标准正确并避免未定义行为的解决方案感兴趣。

以下幼稚的方法不可靠,因为由于浮点舍入,不能保证类型F可以表示std::numeric_limits<I>::max()

#include <cmath>
#include <limits>
#include <type_traits>

template <typename I, typename F>
bool is_safe_conversion(F x)
{
    static_assert(std::is_floating_point_v<F>);
    static_assert(std::is_integral_v<I>);

    // 'fmax' may have a different value than expected
    static constexpr F fmax = static_cast<F>(std::numeric_limits<I>::max());

    return std::abs(x) <= fmax; // this test may gives incorrect results
}
Run Code Online (Sandbox Code Playgroud)

任何的想法?

chu*_*ica 3

有没有一种安全的方法来可靠地确定整数类型 T 是否可以存储浮点整数值 f?

是的。关键是测试是否在使用浮点数学的f范围内- 没有舍入问题。奖励:舍入模式不适用。T::MIN - 0.999...T::MAX + 0.999...

有 3 种失败路径:太大、太小、不是数字。


以下假设int/double. 我将把 C++ 模板的形成留给 OP。

T::MAX + 1 使用浮点数学精确地形成精确值就像梅森数INT_MAX一样容易。(我们这里讨论的不是Mersenne Prime。)

代码利用了:用整数
数学除以 2 的梅森数也是梅森数
整数类型的 2 次幂常量到浮点类型的转换可以肯定是精确的

#define DBL_INT_MAXP1 (2.0*(INT_MAX/2+1)) 
// Below needed when -INT_MAX == INT_MIN
#define DBL_INT_MINM1 (2.0*(INT_MIN/2-1)) 
Run Code Online (Sandbox Code Playgroud)

形成精确值T::MIN - 1是很困难的,因为它的绝对值通常是 2 + 1 的幂,并且整数类型和 FP 类型的相对精度不确定。相反,代码可以减去 2 的精确幂并与 -1 进行比较。

int double_to_int(double x) {
  if (x < DBL_INT_MAXP1) {
    #if -INT_MAX == INT_MIN
    // rare non-2's complement machine 
    if (x > DBL_INT_MINM1) {
      return (int) x;
    }
    #else
    if (x - INT_MIN > -1.0) {
      return (int) x;
    }
    #endif 
    Handle_Underflow();
  } else if (x > 0) {
    Handle_Overflow();
  } else {
    Handle_NaN();
  }
}
Run Code Online (Sandbox Code Playgroud)

FLT_RADIX != 2关于具有非二进制基数 ( )的浮点类型

使用FLT_RADIX = 4, 8, 16 ...,转换也将是准确的。使用 时FLT_RADIX == 10,代码至少精确到 34 位int,因为double必须精确编码 +/-10^10。FLT_RADIX == 10因此,64 位机器的问题int- 风险较低。根据记忆,最后一次FLT_RADIX == 10投产已经是十多年前了。

整数类型始终编码为 2 的补码(最常见)、1 的补码或符号大小。INT_MAX始终是 2 次幂减 1。INT_MIN总是 2 或 1 的 - 次幂。实际上,始终以 2 为底。