【问题标题】:Check if float can be represented as integral type检查浮点数是否可以表示为整数类型
【发布时间】:2015-12-24 23:39:56
【问题描述】:

如何检查 float 是否可以表示为整数类型,而无需通过强制转换来调用未定义的行为? §4.9.1 禁止这样做:

浮点类型的纯右值可以转换为 整数类型。转换截断;也就是小数部分 被丢弃。如果截断的值不能,则行为未定义 以目标类型表示,

对于 C 有 this 问题,但接受的答案显然会导致未定义的行为(首先是简单的强制转换,然后是使用 union hack,这让整个事情对我来说非常有问题)。

我明白很难有一个完全合规的解决方案,但是一个由实现定义的解决方案(假设 IEEE-754 浮动)也是可以接受的。

【问题讨论】:

  • 检查原始值是否小于numeric_limits<desinationType>::max()(明显扩展到负值)还不够吗?一旦它在范围内,那么截断的值应该是可表示的。
  • @Igor 但是比较如何进行呢?据我所知,这将再次涉及一些隐式转换,这会导致错误结果或 UB。,
  • 比较将从整数转换为浮点数。这在典型实现上应该不是问题——即使 2^64 也在 32 位 IEEE float 的范围内。为了更加安全,在比较之前将两个值都转换为double。您需要 1000 位整数类型才能接近 64 位 IEEE double 的限制。
  • 我之前回答过但删除了答案,因为引用的问题似乎有一个很好且符合要求的答案stackoverflow.com/a/17822304/34509
  • @JohannesSchaub-litb:很好的发现,但最好有一个合适的 C++ 版本(通用,不假设特定类型)。因此,我认为不要将其作为重复项关闭。为翻译人员 +1。

标签: c++ undefined-behavior


【解决方案1】:

检查truncf(x) == x。 (函数在<math.h> 中)当且仅当 x 没有小数部分时才会比较 true。然后,将 x 与类型的范围进行比较。

示例代码(未经测试)

#include <cfenv>
#include <cmath>
#pragma STDC FENV_ACCESS on

template<class F, class N> // F is a float type, N integral.
  bool is_representable(const F x)
{
  const int orig_rounding = std::fegetround();

  std::fesetround(FE_TOWARDZERO);
  const bool to_return = std::trunc(x) == x &&
                         x >= std::numeric_limits<N>.min() &&
                         x <= std::numeric_limits<N>.max();
  std::fesetround(orig_rounding);
  return to_return;
}

将舍入设置为零,整数类型的最小值和最大值到浮点类型的隐式转换不应溢出。在包括 i386 在内的许多架构上,转换为 long double 也将提供足够的精度来准确表示 64 位 int。

【讨论】:

  • 您可能想要添加代码来准确显示如何检查该范围,而不会触发溢出。此外,&lt;limits.h&gt; 不如std::numeric_limits
  • 这是一个 C++ 而不是 C 的问题,真的。
【解决方案2】:

您可以使用snprintf() 中的%a 格式类型说明符来访问尾数和指数,然后您可以计算出该数字是否为整数以及它是否适合整数类型一个特定的大小。我在different problem中使用了这个方法。

【讨论】:

    【解决方案3】:

    让我们考虑从 1 到 MAX_FLOAT 的正整数。对于 IEEE 754,指数代表 2 的幂(它与偏差一起存储)。 IEE-754 格式使用 1 个符号位、8 个偏置指数位和 23 个尾数位。尾数 是派系数的一部分。最高位是1/2,下1/4,下1/8……

    从 1 到 1.999... 的数字具有相同的指数。 (1) 范围内有 1 个整数

    从 2 到 3.999...的数字具有相同的指数。 (2,3) 范围内有 2 个整数。 3 设置了最高尾数位,因此它需要前导尾数位。 如果任何其他低尾数位是那么它不是整数。因为是2或3 加上小数位的值。

    从 4 到 7.999... 的数字具有相同的指数。范围内有 4 个整数 (4,5,6,7) 这些使用 2 个最高尾数位。如果设置了任何其他尾数位,则它不是整数。

    从 8 到 15.999 的数字...具有相同的指数。范围内有 8 个整数 (8,9,10,11,12,13,14,15) 这些使用 3 个最高尾数位。如果设置了任何其他尾数位,则它不是整数。

    我希望你能看到随着指数增加的规律,可能的整数数量会翻倍。所以忽略n个最高尾数位并测试是否设置了低位。如果是,则该数字不是整数。

    该表显示了常数指数值 0x40 加上下一个最高位,并且只有高位尾数位被设置为整数

    Float    Hex
    4        0x40800000
    5        0x40a00000
    6        0x40c00000
    7        0x40e00000
    

    将浮点数转换为 UInt32

    float x  = 7.0;
    UInt32 * px = (UInt32*)&x;
    UInt32    i = *px;
    

    【讨论】:

    • 虽然给定的实现违反了严格的别名并不是一个坏主意——虽然这是可以修复的——但遗憾的是,Johannes 删除了他显示相同方法的答案。
    猜你喜欢
    • 2011-08-13
    • 2013-11-29
    • 2013-02-25
    • 1970-01-01
    • 2018-07-08
    • 1970-01-01
    • 2013-12-26
    • 2023-03-14
    相关资源
    最近更新 更多