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

Detect overflow when converting integral to floating types

本文关键字:检测 溢出 浮点型 整型 转换      更新时间:2023-10-16

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

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

有什么方法可以检查最后一种情况吗?在我看来,最后一种不明确的行为是不可避免的。如果我有一个积分值i,并天真地检查类似的东西

i <= FLT_MAX

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

或者,对于整型和浮点型的相对大小,是否有一些保证,这意味着"浮点总是可以表示int的所有值(当然不一定完全是这样(",或者至少"长双精度总是可以容纳所有值",这样我们就可以在该类型中进行比较?不过,我找不到这样的东西。

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

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

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函数可能会产生具有精确整数数学的边条件,并且比较不正确。


宏魔术可以使用如下钝角测试,以利用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,最好的测试是目标有点低。当它为真时,转换将起作用。然而,在下面的测试中报告错误的大整数的小范围变化确实转换正常。

下面是一个更精细的想法,我需要仔细考虑一下,但我希望它能为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
}

这里有一个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();
}

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