在C ++中,如果I
的范围是F
,则将类型static_cast<I>(static_cast<F>(i)) == i
的整数值转换为浮点类型I
是准确的-与F
一样。 static_cast<F>(i)
的整数值范围的一部分。
是否可以(如果使用的话)如何计算template <class F, class I>
bool is_cast_safe(I value)
{
return std::abs(alue) < std::numeric_limits<F>::digits;
}
std::cout << is_cast_safe<float>(4) << std::endl; // true
std::cout << is_cast_safe<float>(0x1000001) << std::endl; // false
的精度损失(不使用范围更广的其他浮点类型)?
首先,我尝试编写一个函数,该函数将在转换是否安全(安全,即不损失精度)的情况下返回,但是我必须承认我不太确定其正确性。
{{1}}
谢谢。
答案 0 :(得分:1)
is_cast_safe
可以通过以下方式实现:
static const F One = 1;
F ULP = std::scalbn(One, std::ilogb(value) - std::numeric_limits<F>::digits + 1);
I U = std::max(ULP, One);
return value % U;
这会将ULP
转换为value
的结果中,将F
设置为最低位数的值。 logbf
返回最高位的位置(作为浮点基数的指数),并减去位数后的一位将调整为最低位。然后scalbn
为我们提供该头寸的值,即ULP。
然后value
可以准确地用F
表示,并且前提是它是ULP的倍数。为了测试这一点,我们将ULP转换为I
(如果小于1,则替换为1),然后将value
的其余部分除以ULP(或1)。
此外,如果担心到F
的转换可能溢出,也可以插入代码来处理。
计算更改的实际金额比较麻烦。到浮点数的转换可以向上或向下取整,并且选择的规则是实现定义的,尽管最常见的舍入关系为偶数。因此,无法根据我们在numeric_limits
中给出的浮点属性来计算实际的变化。它必须涉及执行转换并以浮点数进行一些工作。绝对可以做到这一点,但这很麻烦。我认为一种可行的方法是:
value
为非负数。 (负值可以类似地处理,但为简单起见,现在省略。)F
时是否溢出。这本身很棘手,因为如果值太大,则行为是不确定的。 this answer中提出了一些类似的考虑,以解决有关从浮点到整数(在C中)安全转换的问题。x
。将x
除以浮点基数r
,得到y
。如果y
不是整数(可以使用fmod
或trunc
测试),则转换是准确的。y
转换为I
,生成z
。这是安全的,因为y
小于原始的value
,因此它必须适合I
。(z-value/r)*r + value%r
。答案 1 :(得分:0)
I loss = abs(static_cast<I>(static_cast<F>(i))-i)
应该可以完成这项工作。唯一的例外是i
的大小很大,因此static_cast<F>(i)
会生成超出范围的F
。
(我想这里I abs(I)
可用)