【问题标题】:Loss of precision for int to float conversionint 到 float 转换的精度损失
【发布时间】:2019-03-06 01:47:26
【问题描述】:

在 C++ 中,如果 I 的范围是整数值范围的一部分,那么将 I 类型的整数值转换为浮点类型 F 将是精确的 — 与 static_cast<I>(static_cast<F>(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

提前致谢。

【问题讨论】:

  • 转成float再转回int,看看你得到的是不是你开始的。
  • 您可能需要在这里考虑一件事:如今,几乎所有东西都使用 IEEE-754 浮点数。所以这里没有必要棘手,众所周知,此时 int->float 转换开始失去精度。有is_iec559,可用于检查IEEE-754一致性。

标签: c++ floating-point type-conversion


【解决方案1】:

I loss = abs(static_cast&lt;I&gt;(static_cast&lt;F&gt;(i))-i) 应该可以完成这项工作。唯一的例外是i 的幅度很大,因此static_cast&lt;F&gt;(i) 会生成超出I 范围的F

(我在这里假设I abs(I) 可用)

【讨论】:

  • 如果static_cast&lt;F&gt;(i) 向上取整以超过整数类型的最大值,则会失败。比如i是一个int32_t,值为2147483647,F是普通的32位float,那么static_cast&lt;F&gt;(i)就是2147483648,static_cast&lt;I&gt;(static_cast&lt;F&gt;(i))会溢出,行为未定义.
  • @EricPostpischil:是的,这就是我所说的“唯一例外......”这句话的意思。但你已经解释得更好了:)
【解决方案2】:

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 的结果中的最小数字位置的值。 ilogb 返回最高位数位置的位置(作为浮点基数的指数),并且位数减去 1 调整到最低位数位置。然后scalbn 为我们提供了该位置的值,即 ULP。

那么当且仅当 value 是 ULP 的倍数时,F 才能精确表示。为了测试这一点,我们将 ULP 转换为 I(但如果小于 1,则替换为 1),然后将 value 的余数除以 ULP(或 1)。

另外,如果担心到F 的转换可能会溢出,也可以插入代码来处理这个问题。

计算实际变化量比较棘手。到浮点的转换可以向上或向下舍入,并且选择的规则是实现定义的,尽管舍入到最近的关系到偶数是常见的。所以实际的变化不能从我们在numeric_limits中给出的浮点属性中计算出来。它必须涉及执行转换并在浮点中做一些工作。这绝对可以做到,但很麻烦。我认为应该可行的方法是:

  • 假设value 是非负数。 (负值可以类似地处理,但为简单起见暂时省略。)
  • 首先,测试转换为F 的溢出。这本身就很棘手,因为如果值太大,行为是不确定的。在this answer 中针对有关安全地将浮点数转换为整数(在 C 中)的问题提出了一些类似的考虑。
  • 如果值没有溢出,则转换它。让结果为x。将x 除以浮点基数r,得到y。如果y 不是整数(可以使用fmodtrunc 进行测试),则转换是准确的。
  • 否则,将y 转换为I,生成z。这是安全的,因为y 小于原始value,所以它必须适合I
  • 那么由于转换导致的错误是(z-value/r)*r + value%r

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-12-17
    • 1970-01-01
    • 2022-09-20
    • 1970-01-01
    • 2015-07-27
    • 1970-01-01
    • 2019-06-01
    相关资源
    最近更新 更多