繁体   English   中英

当 64 位 int 在 C/C++ 中转换为 64 位浮点数并且没有完全匹配时,它是否总是落在非小数上?

[英]When a 64bit int is cast to 64bit float in C/C++ and doesn't have an exact match, will it always land on a non-fractional number?

当 int64_t 被强制转换为 double 并且没有完全匹配时,据我所知,我得到了一种等效于 double 的尽力而为最近值。 例如, int64_t 中的 9223372036854775000 似乎以双精度形式结束:

#include <stdio.h>

int main(int argc, const char **argv) {
    printf("Corresponding double: %f\n", (double)9223372036854775000LL);
    // Outputs: 9223372036854774784.000000
    return 0;
}

在我看来,好像将 int64_t 强制转换为 double 总是以干净的非小数结尾,即使在 double 精度非常低的更高数字范围内也是如此。 但是,我只是从随机尝试中观察到这一点。 对于任何转换为双精度的 int64_t 值,是否保证会发生这种情况?

如果我将这个非小数双精度转换回 int64_t,我是否总是会得到精确对应的 64 位 int 并将 .0 切掉? 假设它在转换回来的过程中没有溢出。 )就像这里:

#include <inttypes.h>
#include <stdio.h>

int main(int argc, const char **argv) {
    printf("Corresponding double: %f\n", (double)9223372036854775000LL);
    // Outputs: 9223372036854774784.000000
    printf("Corresponding int to corresponding double: %" PRId64 "\n",
           (int64_t)((double)9223372036854775000LL));
    // Outputs: 9223372036854774784
    return 0;
}

还是在某些极端情况下它可能不精确并让我得到“错误”的整数?

直观地说,从我的测试来看,这两点的答案似乎都是“是”,但如果有人对浮点标准及其背后的数学有很好的正式理解,那么这对我来说真的很有帮助。 如果已知任何已知的更积极的优化(如 gcc 的-Ofast )会破坏其中任何一个,我也会很好奇。

在一般情况下是的,两者都应该是真的。 The floating point base needs to be - if not 2, then at least integer and given that, an integer converted to nearest floating point value can never produce non-zero fractions - either the precision suffices or the lowest-order integer digits in the base浮动类型的将被归零。 例如,在您的情况下,您的系统使用 ISO/IEC/IEEE 60559 二进制浮点数。 在以 2 为底进行检查时,可以看出该值的尾随数字确实为零:

>>> bin(9223372036854775000)
'0b111111111111111111111111111111111111111111111111111110011011000'
>>> bin(9223372036854774784)
'0b111111111111111111111111111111111111111111111111111110000000000'

考虑到 double 的值落在 integer 类型的范围内,将不带小数的 double 转换为 integer 类型应该是精确的......

尽管您仍然可能会遇到实现质量问题或彻底的错误 - 例如, MSVC当前有一个编译器错误,其中设置了 MSB 的无符号 32 位值的往返转换(或只是 2³¹ 和 2³² 之间的双精度值-1 转换为unsigned int ) 将在转换中“溢出”,并且总是导致正好 2³¹。

以下假设被转换的值为正。 负数的行为是类似的。

C 2018 6.3.1.4 2 指定从 integer 到真实的转换并说:

…如果要转换的值在可以表示但不能精确表示的值范围内,则结果是最接近的较高或最近的较低可表示值,以实现定义的方式选择。

这告诉我们,仅当边界x的两个可表示值之一不是 integer 并且x不可表示时,某些 integer 值x被转换为浮点数才能产生非整数。

5.2.4.2.2 指定了用于浮点数的 model。 每个有限浮点数都由某个基数b中的数字序列表示,该数字序列针对某个指数eb e缩放。 b是大于 1 的 integer 。)然后,如果限制x的两个值之一,例如p不是 integer,则缩放比例必须使得该浮点数中的最低位表示分数。 但如果是这种情况,则将p中表示分数的所有数字设置为 0 必须生成一个新的浮点数,即 integer。 如果x < p ,这个 integer 必须是x ,因此x可以用浮点格式表示。 另一方面,如果p < x ,我们可以将足够的数字加到表示分数的每个数字上,使其为 0(并产生下一个更高数字的进位)。 这也将产生一个 integer 可表示为浮点类型1 ,它必须是x

因此,如果将 integer x转换为浮点类型会产生非整数,则x必须可以在该类型中表示。 但是随后转换为浮点类型必须产生x 所以永远不可能产生一个非整数。

脚注

1这可能会执行所有数字,例如将其应用于三位十进制数 9.99,产生 10.00。 在这种情况下,如果它在浮点格式的范围内,则生成的值是b的下一个幂。 如果不是,则 C 标准未定义该行为。 另请注意,C 标准对浮点格式必须支持的范围设置了最低要求,这排除了任何格式无法表示 1,这避免了转换可能产生类似 999 的数字的退化情况,因为它是最大可表示的有限值。

当 64 位int被转换为 64 位浮点数......并且没有完全匹配时,它是否总是落在非小数上?
对于任何转换为doubleint64_t值,这是否保证会发生?

对于common double :是的,它总是落在一个非小数上

当不匹配时,结果是上面或下面最接近的浮点可表示值,具体取决于舍入模式。 鉴于 common double的特性,这两个边界值也是整数。 当该值不可表示时,首先有一个附近的整数 1。


...如果我将这个非小数double精度转换回int64_t ,我是否总是会得到精确对应的 64 位int并将 .0 切掉?

不会。 INT64_MAX附近的边缘情况会失败,因为转换后的值可能会变成高于INT64_MAX的 FP 值。 然后转换回 integer 类型会导致:“新类型是有符号的,并且值不能在其中表示;结果是实现定义的,或者引发了实现定义的信号。” C17dr § 6.3.1.3 3

#include <limits.h>
#include <string.h>

int main() {
  long long imaxm1 = LLONG_MAX - 1;
  double max = (double) imaxm1;
  printf("%lld\n%f\n", imaxm1, max);
  long long imax = (long long) max;
  printf("%lld\n", imax);
}

9223372036854775806
9223372036854775808.000000
9223372036854775807  // Value here is implementation defined.

更深层次的例外

(问题变体)当 N 位 integer 类型被强制转换为浮点并且没有精确匹配时,它是否总是落在非小数上?

Integer 类型范围超过有限浮点

转换为无穷大:使用常见的floatuint128_tUINT128_MAX转换为无穷大 这很容易通过超宽 integer 类型实现。

int main() {
  unsigned __int128  imaxm1 = 0xFFFFFFFFFFFFFFFF;
  imaxm1 <<= 64;
  imaxm1 |= 0xFFFFFFFFFFFFFFFF;
  double fmax = (float) imaxm1;
  double max = (double) imaxm1;
  printf("%llde27\n%f\n%f\n", (long long) (imaxm1/1000000000/1000000000/1000000000), 
    fmax, max);
}

340282366920e27
inf
340282366920938463463374607431768211456.000000

浮点进动深度超过范围

在某些 unicorn 实现中,FP 精度非常宽且范围小,最大的有限项在理论上(而不是实践)可能是非整数。 然后使用更宽的 integer 类型,转换可能会导致这个非整数值。 我不认为这是 OP 的合法问题。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM