【问题标题】:Detect overflow when converting integral to floating types将整数转换为浮点类型时检测溢出
【发布时间】:2017-08-28 20:29:48
【问题描述】:

据我所知,C++ 也依赖于这些问题的 C 标准有以下部分:

当一个整数类型的值被转换为一个真正的浮点类型时,如果被转换的值可以在新的类型中精确地表示,那么它是不变的。如果要转换的值在可以表示但不能准确表示的值范围内,则结果是最接近的较高或最近的较低可表示值,以实现定义的方式选择。如果要转换的值超出可表示的值范围,则行为未定义。

有什么方法可以检查最后一个案例吗?在我看来,最后一个未定义的行为是不可避免的。如果我有一个整数值i 并天真地检查类似

i <= FLT_MAX

我将(除了与精度相关的其他问题)已经触发它,因为比较首先将i 转换为float(在这种情况下或通常转换为任何其他浮动类型),所以如果它超出范围内,我们得到未定义的行为。

或者是否有一些关于整数和浮点类型的相对大小的保证,这意味着“float 总是可以表示 int 的所有值(当然不一定完全正确)”或至少“long double 总是可以容纳一切”这样我们就可以在那种类型中进行比较?不过,我找不到类似的东西。

这主要是一个理论练习,所以我对“在大多数架构上这些转换总是有效”的答案不感兴趣。让我们尝试找到一种方法来检测这种溢出,而不假设任何超出 C(++) 标准的东西! :)

【问题讨论】:

  • 64 位 IEEE 浮点可以表示任何
  • 这个问题很有趣。我认为它不能以通用方式解决。对于初学者来说,Standard 甚至没有强制要求 IEEE 754 用于浮点数。我认为,您最终会得到特定于实现的逻辑,它知道浮点数是如何表示的以及它们的值是什么。
  • @cdhowie 不要忘记浮点有效数比存储的多一位,因为归一化有效数的 m.s.位始终为1,因此是隐含的(值0 除外)。
  • @old_timer uintmax_t 作为 128 位类型,其值刚好超过 FLT_MAX(溢出)是一种真正的可能性,对于使用如此宽的整数和窄 FP。
  • @curiousguy 您没有阅读整个语句,您不能从整数转换中溢出浮点数。现在,我确实以半精度证明了这一点,但是使用典型的整数(32 位、64 位)单上就不会溢出。您可以/将会失去精确度,但这是不同的,这是人们感到困惑的关键。引用的文本以及导致它的情况很容易理解......但实现定义为人们所期望的,否则他们将不得不再写数百页......

标签: c++ c floating-point language-lawyer undefined-behavior


【解决方案1】:

将整数转换为浮点类型时检测溢出

FLT_MAXDBL_MAX 根据 C 规范至少为 1E+37,因此所有具有 |values| 的整数在所有兼容的平台上,122 位或更少的将转换为float 而不会溢出。与double相同


在一般情况下解决 128/256/等整数的问题。位,FLT_MAXsome_big_integer_MAX 都需要减少。

也许通过记录两者的日志。 (bit_count() 是待定用户代码)

if(bit_count(unsigned_big_integer_MAX) > logbf(FLT_MAX)) problem();

或者如果整数缺少填充

if(sizeof(unsigned_big_integer_MAX)*CHAR_BIT > logbf(FLT_MAX)) problem();

注意:使用像 logbf() 这样的 FP 函数可能会产生具有精确整数数学的边缘条件和不正确的比较。


Macro magic 可以使用像下面这样的钝测试,利用 BIGINT_MAX 肯定是 2 的幂负 1 和 FLT_MAX 除以 2 的幂肯定是精确的(除非FLT_RADIX == 10)。

如果从大整数类型到float 的转换将是不精确的对于一些大整数,此预处理器代码将抱怨

#define POW2_61 0x2000000000000000u  
#if BIGINT_MAX/POW2_61 > POW2_61
  // BIGINT is at least a 122 bit integer 
  #define BIGINT_MAX_PLUS1_div_POW2_61  ((BIGINT_MAX/2 + 1)/(POW2_61/2))
  #if BIGINT_MAX_PLUS1_div_POW2_61 > POW2_61
    #warning TBD code for an integer wider than 183 bits
  #else
    _Static_assert(BIGINT_MAX_PLUS1_div_POW2_61 <= FLT_MAX/POW2_61, 
        "bigint too big for float");
  #endif
#endif

[编辑 2]

有什么方法可以检查最后一个案例吗?

如果从大整数类型到float 的转换将是不精确的对于选择的大整数,此代码将抱怨

当然,测试需要在尝试转换之前进行。

鉴于各种舍入模式或罕见的FLT_RADIX == 10,可以轻松获得的最佳结果是目标有点低的测试。当它为真时,转换将起作用。然而,在以下测试中报告为 false 的 变化较小 范围内的大整数确实可以转换。

下面是一个更精致的想法,我需要仔细考虑一下,但我希望它为 OP 正在寻找的测试提供一些编码想法。

#define POW2_60 0x1000000000000000u
#define POW2_62 0x4000000000000000u
#define MAX_FLT_MIN 1e37
#define MAX_FLT_MIN_LOG2 (122 /* 122.911.. */)

bool intmax_to_float_OK(intmax_t x) {
#if INTMAX_MAX/POW2_60 < POW2_62
  (void) x;
  return true; // All big integer values work
#elif INTMAX_MAX/POW2_60/POW2_60 < POW2_62
  return x/POW2_60 < (FLT_MAX/POW2_60) 
#elif INTMAX_MAX/POW2_60/POW2_60/POW2_60 < POW2_62
  return x/POW2_60/POW2_60 < (FLT_MAX/POW2_60/POW2_60) 
#else
  #error TBD code
#endif
}

【讨论】:

  • @Julian Kniephoff 您的帖子需要更多帮助吗?
【解决方案2】:

这是一个 C++ 模板函数,它返回适合两种给定类型的最大正整数。

template<typename float_type, typename int_type>
int_type max_convertible()
{
    static const int int_bits = sizeof(int_type) * CHAR_BIT - std::is_signed<int_type>() ? 1 : 0;
    if ((int)ceil(std::log2(std::numeric_limits<float_type>::max())) > int_bits)
        return std::numeric_limits<int_type>::max();
    return (int_type) std::numeric_limits<float_type>::max();
}

如果您要转换的数字大于此函数的返回值,则无法转换。不幸的是,我很难找到用于测试它的类型组合,很难找到不适合最小浮点类型的整数类型。

【讨论】:

  • "最大可表示整数"
  • @cdhowie 我已经更改了该声明的措辞。我真的需要明确说明较小的整数也适合吗?似乎应该自动跟随。
  • 我不认为它会遵循。我确信有更大的整数可以精确表示,但不是连续整数。
  • @cdhowie 好的,我明白你现在在说什么了。该问题指定了 3 个不同的范围:可以表示 所有 个连续值的范围、可以进行一些舍入的范围以及导致未定义行为的范围。只要求第三种情况的答案,就是这样。
  • 公平点,出于某种原因,我认为问题是在询问第二种情况。哎呀。
猜你喜欢
  • 2018-04-06
  • 1970-01-01
  • 2019-01-05
  • 1970-01-01
  • 2011-05-18
  • 1970-01-01
  • 2021-08-08
  • 1970-01-01
相关资源
最近更新 更多