【问题标题】:When is integer to floating point conversion lossless?整数到浮点转换何时无损?
【发布时间】:2020-08-04 09:52:50
【问题描述】:

如果int32_t 总是无损地转换为double,我特别感兴趣。

以下代码是否总是返回true

int is_lossless(int32_t i)
{
    double   d = i;
    int32_t i2 = d;
    return (i2 == i);
}

int64_t 是什么?

【问题讨论】:

  • int64_t 显然不是这样,因为尾数只有 52 位大小。但是对于int32_t,这应该是无条件的。

标签: c++ c floating-point


【解决方案1】:

整数到浮点数的转换何时无损?

当浮点类型有足够的精度和范围来编码整数类型的所有可能值时。

以下int32_t 代码是否总是返回true? --> 是的。
以下int64_t 代码是否总是返回 true? --> 没有。

由于DBL_MAX 至少为 1E+37,范围至少足以满足int122_t,让我们看一下精度。

使用普通的double,其底数为2、符号位、53 位有效位和指数,可以精确表示int54_t 及其53 个值位的所有值。 INT54_MIN 也可以表示。有了这个double,它就有DBL_MANT_DIG == 53,在这种情况下,它是浮点有效数中以2为底的位数。

最小的不可表示值是INT54_MAX + 2int55_t 和更宽的类型的值不能完全表示为 double

对于 uintN_t 类型,还有 1 个值位。然后典型的double 可以编码所有uint53_t 和更窄。


使用其他可能的double 编码,如C 指定DBL_DIG >= 10int34_t 的所有值都可以往返。

无论double 编码如何,int32_t 的代码始终为真。


int64_t 是什么?

int64_t 的 UB 潜力。

int64_t i ... double d = i; 中的转换在不精确时会产生 2 个最近候选的实现定义结果。这通常是四舍五入到最近的。然后,INT64_MAX 附近的i 值可以转换为比INT64_MAX 多一个的double

对于int64_t i2 = d;,将比INT64_MAX 大一的double 值转换为int64_t未定义的行为 (UB)。

一个简单的先前测试来检测这一点:

#define INT64_MAX_P1 ((INT64_MAX/2 + 1) * 2.0)
if (d == INT64_MAX_P1) return false;  // not lossless

【讨论】:

    【解决方案2】:

    问题:以下代码总是返回true吗?

    始终是一个重要的声明,因此答案是

    C++ 标准没有提及 C++ 已知的浮点类型(floatdoublelong double)是否属于 IEEE-754 类型。该标准明确规定:

    共有三种浮点类型:floatdoublelong double。 double 类型提供的精度至少与 float 一样,long double 类型提供的精度至少与 double 一样。 float 类型的值集是 double 类型的值集的子集; double 类型的值集是 long double 类型的值集的子集。浮点类型的值表示是实现定义的。 [注:本文档对浮点运算的准确性没有要求;另见 [support.limits]。 — 尾注] 整数和浮点类型统称为算术类型。标准库模板std​::​numeric_­limits 的特化应指定实现的每种算术类型的最大值和最小值。

    来源:C++ standard: basic fundamentals

    最常见的类型double代表IEEE 754双精度二进制浮点格式binary64,可以表示为:

    并解码为:

    但是,还有大量其他 floating-point formats 以不同方式解码,并且不一定具有与众所周知的 IEEE-754 相同的属性。尽管如此,它们都非常相似:

    • 它们有 n 位长
    • 一位代表符号
    • m 位表示有或没有隐藏第一位的重要
    • e 位表示给定底数(2 或 10)的某种形式的指数

    要知道 double 是否可以表示所有 32 位有符号整数,您必须回答以下问题(假设我们的浮点数以 2 为底):

    1. 我的浮点表示是否在有效位中有隐藏的第一位?如果是,假设 m=m+1
    2. 32 位有符号整数由 1 个符号位和 31 个表示数字的位表示。是否足够大以容纳这 31 位?
    3. 指数是否足够大,可以表示 1.xxxxx 2^31 形式的数字?

    如果您对最后两个问题的回答是肯定的,那么int32 始终可以由在此特定系统上实现的double 表示。

    注意:我忽略了decimal32decimal64 数字,因为我对它们没有直接的了解。

    【讨论】:

    • C/C++ 需要某些 FP 要求,涉及精度和范围。即使没有 IEEE-754 合规性,int32_t 也可以精确地往返于double。考虑到DBL_DIG >= 10DBL_MAX >= 1e37 的最低语言要求。回复:“一个双精度可以表示所有 32 位有符号整数或不”,要求 1 是一个实现细节。 Reg #2 根据语言要求,double 是肯定的。 3. 根据语言要求也是可以的。
    • @chux 你能给我指出一个参考吗?我一直在浏览标准,找不到floatdoublelong double 的任何最低要求。我确实找到了一些整数类型。
    • 搜索“5.2.4.2.2 浮动类型的特征”
    • 所以通常浮点只能无损地保存 m+2 位整数(m 没有隐藏的第一位)。
    • 我们还应该考虑极端情况的值。 int16_t 最大值为 32 767,但最小值为 -32 768。如果 m == 14,则无法存储此 -32 768。但它实际上可以,因为 -32 768 == 1.000 * 2^15(其中 1.000 是分数,15 是指数)。跨度>
    【解决方案3】:

    注意:我的回答假设 double 遵循 IEEE 754,并且 int32_tint64_t 都是 2 的补码。

    下面的代码总是返回true吗?

    double 的尾数/有效位长于 32b,因此 int32_t => double 始终没有错误,因为没有可能的精度错误(并且没有可能的上溢/下溢,指数覆盖超过所需的值范围)

    int64_t 是什么?

    但是double 的 53 位尾数/有效位(包括 1 个隐式)不足以保存 int64_t 的 64b => int64_t 具有足够远的高位和低位不能存储在 @987654329 中@ 没有精度错误(仍然没有可能的上溢/下溢,指数仍然覆盖超过所需的值范围)

    【讨论】:

    • @MaximEgorushkin 您能否提供一个无法存储在double 中的 32 位整数示例? (无论如何抱歉,31实际上存储为1.1111b位移4,小数点前隐含1。)
    • @MaximEgorushkin:这个答案中的措辞是正确的。 double 的有效位超过 32 位这一事实意味着 double 具有足够的精度来精确表示任何 32 位整数。该答案并未说明整数“完全”存储在有效数字中。确实,指数必须有足够的范围,但这是一个小问题,已得到解决。
    • 请注意,浮点表示的小数部分的首选术语是“有效位”,而不是 IEEE-754 标准中使用的“尾数”。 “尾数”是对数的小数部分。有效数字是线性的;将有效数乘以 1.1 将表示的值乘以 1.1。尾数是对数的;添加到尾数乘以表示的值。
    • @MaximEgorushkin:不,它没有。答案表明有效数字为任务提供了足够的精度。确实如此。 1 和 2 以相同的有效位存储这一事实是无关紧要的;在每种情况下,有效数字都为任务提供了足够的精度,这就是这个答案中所说的。您对语言的理解完全不正确。
    • @bruno: "但也隐含地用于补码 2 整数" 不,它显式地用于 2 的补码整数,因为所有的 @987654333标准要求 @ 类型是 2 的补码。其他整数类型(C++20 之前)可能是也可能不是 2 的补码,但如果由实现提供,那些特定类型必须是 2 的补码。
    【解决方案4】:

    如果您的平台对double 使用IEEE754,那么可以,任何int32_t 都可以在double 中完美表示。这不是int64_t 可以具有的所有可能值的情况。

    (在某些平台上可以调整浮点类型的尾数/指数大小以使转换有损,但这样的类型不会是 IEEE754 double。)

    要测试 IEEE754,请使用

    static_assert(std::numeric_limits<double>::is_iec559, "IEEE 754 floating point");
    

    【讨论】:

    • 使用 IEEE-754 是不够的。 double 使用 IEEE-754 binary64 就足够了。
    • C++ 实现可能选择不断言is_iec559,因为它的算术和其他行为不完全符合,即使它使用 IEEE-754 binary64 格式作为双精度。 &lt;numeric_limits&gt; 模板中提供的各种值可用于测试类型中是否有足够的精度来表示 int32_t 的所有值。
    猜你喜欢
    • 1970-01-01
    • 2018-07-08
    • 2019-11-25
    • 2011-06-17
    • 1970-01-01
    • 2018-08-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多