【问题标题】:When Will static_casting the Result of ceil Compromise the Result?static_casting ceil 的结果什么时候会影响结果?
【发布时间】:2018-01-05 16:04:03
【问题描述】:

static_casting 从浮点到整数只是去掉了数字的小数点。例如static_cast<int>(13.9999999) 产生13

并非所有整数都可以表示为浮点数。例如,在内部,与 13,000,000 最接近的 float 可能是:12999999.999999

在这个假设的情况下,我希望得到一个意想不到的结果:

const auto foo = 12'999'999.5F;
const auto bar = static_cast<long long>(ceil(foo));

我的假设是,如果不一定在 13,000,000 次,这种崩溃确实会发生在某个时间点。我只想知道我可以信任static_cast&lt;long long&gt;(ceif(foo)) 的范围?

【问题讨论】:

  • “最大的可表示浮点值是所有标准浮点格式中的精确整数,...”来自:en.cppreference.com/w/cpp/numeric/math/ceil
  • @RichardCritten 先生,你能解释一下我的回答吗?
  • 这将由实现定义,但对于 IEEE 754 有一个欺骗目标:stackoverflow.com/questions/3793838/…
  • @NathanOliver 那么你有没有机会使用一些 IEEE 魔法来解释我的答案?这甚至在int 的范围内,这很讨厌。
  • @NathanOliver 所以我添加了一个new answer,这解释了为什么ceil 在大于(1LL &lt;&lt; numeric_limits&lt;T&gt;::digits - 1LL) - 1LL 的数字上不起作用。希望它是清晰的:/

标签: c++ floating-point integer static-cast ceil


【解决方案1】:

例如,在内部,最接近 13,000,000 的浮点数可能是:12999999.999999。

这在任何正常的浮点格式中都是不可能的。数字的浮点表示等价于Mbe,其中b em> 是固定基数(例如,2 表示二进制浮点),Me 是整数,它们的值有一些限制。为了表示像 13,000,000-x 这样的值,其中 x 是小于 1 的某个正值,e 必须为负(因为Mbe 对于非负 e 是一个整数)。如果是这样,那么M•b0 是一个大于Mb 的整数e,所以大于13,000,000,所以13,000,000可以表示为M'b0 ,其中 M' 是小于 M 的正整数,因此适合 M 的允许值范围(在任何普通浮点格式)。 (也许一些奇怪的浮点格式可能会在 Me 上施加一个奇怪的范围来防止这种情况发生,但没有正常格式可以。)

关于您的代码:

auto test = 0LL;
const auto floater = 0.5F;

for(auto i = 0LL; i == test; i = std::ceil(i + floater)) ++test;

cout << test << endl;

i 为 8,388,608 时,8,388,608 + .5 的数学结果为 8,388,608.5。这在您系统上的 float 格式中无法表示,因此它被四舍五入为 8,388,608。其中ceil 是 8,388,608。此时,test 为 8,388,609,因此循环停止。所以这段代码并没有证明 8,388,608.5 是可表示的,而 8,388,609 是不可表示的。

如果我这样做,行为似乎会恢复正常:ceil(8'388'609.5F),它将正确返回 8,388,610。

8,388,609.5 在您的系统上无法以float 格式表示,因此它是按照“四舍五入,接近偶数”的规则进行四舍五入的。两个最接近的可表示值是 8,388,609 和 8,388,610。由于它们相距甚远,因此结果为 8,388,610。该值被传递给ceil,当然返回了 8,388,610。

在 Visual Studio 2015 上,我得到了 8,388,609,这是一个可怕的小安全范围。

在 IEEE-754 基本 32 位二进制格式中,从 -16,777,216 到 +16,777,216 的所有整数都是可表示的,因为该格式具有 24 位有效位。

【讨论】:

    【解决方案2】:

    Floating point numbers 由 3 个 整数 表示,cbq 其中:

    • c 是尾数(所以对于数字:12,999,999.999999 c 将是 12,999,999,999,999
    • q 是指数(所以对于数字:12,999,999.999999 q 将是 -6
    • b 是基数(IEEE-754 要求 b102;在上面的表示中 b 是 10)

    由此不难看出,具有表示 12,999,999.999999 能力的浮点也具有使用 c 表示 13,000,000.000000 的能力1,300,000,000,000q -5

    这个例子有点做作,因为选择的 b10,而在几乎所有实现中选择的基数都是 2。但值得指出的是,即使 b2q 也可以作为尾数的左移或右移。


    接下来让我们在这里讨论一个范围。显然,一个 32 位浮点数不能代表一个 32 位整数表示的所有整数,因为浮点数还必须表示很多更大或更小的数字。由于指数只是移动尾数,因此浮点数总是可以精确表示可以由其尾数表示的每个整数。给定传统的 IEEE-754 二进制基浮点数:

    • 32 位 (float) 具有 24 位尾数,因此它可以表示 [-16,777,215, 16,777,215] 范围内的所有整数
    • 64 位 (double) 具有 53 位尾数,因此它可以表示 [-9,007,199,254,740,991, 9,007,199,254,740,991] 范围内的所有整数
    • A 128-bit (long double depending upon implementation) has a 113-bit mantissa so it can represent all integers in the range [-103,845,937,170,696,552,570,609,926,584,40,191, 103,845,937,170,696,552,570,609,926,584,40,191]

    [source]

    提供digits 作为为给定浮点类型查找此数字的方法。 (尽管不可否认,即使 long long 太小,也无法表示 113 位尾数。)例如,float 的最大尾数可以通过以下方式找到:

    (1LL << numeric_limits<float>::digits) - 1LL
    

    在彻底解释完尾数之后,让我们重温指数部分,谈谈floating point is actually stored 的原理。以 13,000,000.0 为例,可以表示为:

    • c = 13, q = 6, b = 10
    • c = 130, q = 5, b = 10
    • c = 1,300, q = 4, b = 10

    等等。对于传统的二进制格式 IEEE-754 要求:

    通过选择在所选字长和格式内保留最高有效位 (MSB) 的最小可表示指数,使表示变得唯一。另外,指数不直接表示,而是加了一个偏差,使得最小可表示的指数表示为1,0表示次正规数

    如果我们的尾数有 14 个小数位,为了用更熟悉的 base-10 来解释这一点,实现如下所示:

    • c = 13,000,000,000,000 所以 MSB 将用于表示的数字
    • q = 6 这有点令人困惑,这是这里引入的偏差的原因;逻辑上 q = -6 但设置了偏差,以便当 q = 0 只有 c 的 MSB紧靠小数点左侧,表示 c = 13,000,000,000,000, q = 0, b = 10 将表示 1.3
    • b = 10 上面的规则实际上只对 base-2 是必需的,但我已经展示了它们,因为它们将适用于 base-10 以进行解释

    转换回base-2 这意味着numeric_limits&lt;T&gt;::digits - 1q 在小数点后只有零。 ceil 仅在数字中有小数部分时才有效。

    这里的最后一点解释是ceil 将产生影响的范围。在浮点的指数大于numeric_limits&lt;T&gt;::digits 之后继续增加它只会在结果数字中引入尾随零,因此当q 大于或等于numeric_limits&lt;T&gt;::digits - 2LL 时调用ceil。由于我们知道 c 的 MSB 将用于数字,这意味着 c 必须小于(1LL &lt;&lt; numeric_limits&lt;T&gt;::digits - 1LL) - 1LL 因此ceil 对传统二进制 IEEE-754 浮点数:

    • 32 位 (float) 必须小于 8,388,607
    • 64 位 (double) 必须小于 4,503,599,627,370,495
    • 128 位(long double 取决于实现)必须小于 5,192,296,858,534,827,628,530,496,329,220,095

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-02-18
      • 2016-11-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-28
      • 2016-04-05
      • 1970-01-01
      相关资源
      最近更新 更多