浮点数在内存中如何存储?

55

我读到过它们以尾数和指数的形式存储

我读过这篇文章,但我一个都没懂。


1
你链接的文档已经很清楚地解释了。你具体觉得难以理解的是什么? - Michael Borgwardt
4
不,这并不清楚。在引入需要解释的问题之后(“但如果数字是零呢?...哦天啊”),才解释了指数是如何存储的。这就像那些犯罪故事一样,诀窍在于他们没有向你展示所有信息,但故事中的主角却知道所有信息。 - xanatos
这个链接结合了被接受的答案,对于理解非常有帮助:https://www.h-schmidt.net/FloatConverter/IEEE754.html。 - Jonathan M
7个回答

83
要了解它们的存储方式,您首先必须了解它们是什么以及它们旨在处理哪种值。与整数不同,浮点值旨在表示极小值和极大值。对于普通的32位浮点值,这对应于范围从1.175494351 * 10^-38到3.40282347 * 10^+38的值。显然,仅使用32位无法存储这些数字中的每个数字。当涉及表示时,您可以将所有正常浮点数视为范围在1.0到(几乎)2.0之间的值,缩放为二的幂。所以:1.0只是1.0 * 2^0,2.0是1.0 * 2^1,而-5.0是-1.25 * 2^2。那么,为了尽可能高效地对其进行编码,我们真正需要什么?我们究竟需要什么?表达式的符号,指数和1.0到(几乎)2.0之间的值。这称为“尾数”或“有效数字”。根据IEEE-754浮点标准,这是如下编码: 符号只占一个比特。 指数存储为无符号整数,对于32位浮点值,该字段为8位。 1表示最小的指数,“全1-1”表示最大的指数。(0和“全1”用于编码特殊值,请参见下文。)中间的一个值(在32位情况下为127)表示零,这也被称为“偏置”。
  • 当查看尾数(即在1.0和(几乎)2.0之间的值)时,可以看到所有可能的值都以“1”开头(无论是十进制还是二进制表示)。这意味着存储它是没有意义的。其余的二进制位被存储在整数字段中,在32位情况下,该字段为23位。
  • 除了常规浮点数值外,还有一些特殊值:

    • 零用指数和尾数均为零编码。符号位用于表示“正零”和“负零”。当操作结果非常小但仍然重要时,负零非常有用,因为需要知道操作来自哪个方向。
    • 正无穷和负无穷--使用“全部是1”的指数和零尾数字段来表示。
    • 非数字(NaN)--使用“全部是1”的指数和非零尾数来表示。
    • 非规格化数--比最小规格化数还小的数。使用零指数字段和非零尾数来表示。这些数字的特殊之处在于精度(即一个数字能够包含的位数)将随着值变得越来越小而降低,因为尾数中没有空间容纳它们。

    最后,以下是一些具体示例(所有值均为十六进制):

    • 1.0:3f800000
    • -1234.0:c49a4000
    • 100000000000000000000000.0:65a96816

    是inf、-inf、NaN、zero只是符号还是有任何意义?因为所有指数都相同且尾数为零实际上等于1,而所有指数都为1且尾数不为零实际上返回一个合法值... - Akshay
    @Akshay,Inf,-Inf,NaN,Zero和- Zero都具有不同的编码,当应用于浮点运算时,行为是明确定义的。实际上,NaN可以以许多不同的方式进行编码,其中实现可以对不同的变体赋予含义。(符号取决于您使用的编程语言。) - Lindydancer

    15

    7
    +1 但维基百科并不是正式标准,它最多只是一个正式标准的解释 :-) :-) - xanatos
    4
    C语言不需要使用IEEE浮点数。 - Keith Thompson

    9
      typedef struct {
          unsigned int mantissa_low:32;     
          unsigned int mantissa_high:20;
          unsigned int exponent:11;        
          unsigned int sign:1;
        } tDoubleStruct;
    
    double a = 1.2;
    tDoubleStruct* b = reinterpret_cast<tDoubleStruct*>(&a);
    

    以下是一个示例,展示了如果编译器使用IEEE 754双精度(在小端系统上,默认为C double)时内存的设置方式(例如Intel x86)。

    这是以C二进制形式表示的,最好阅读维基百科关于双精度的介绍以理解它。


    1
    那是一种可能性,但不是唯一的。 - Keith Thompson
    Lindydancer提到,在IEEE规范中,有效数字比存储的位数多一个(除了值为0.0的情况)。这是因为有效数字被标准化了,也就是说,在计算后向左移位(指数减少),直到其最高位为“1”。这样可以存储尽可能多的有效位数。但是,由于最高位已知为“1”,因此不会存储它,有效数字进一步标准化了一位,并获得了额外的“虚拟”存储位。有效数字不是52位,而是53位。 - Weather Vane
    C++结构体仅显示双精度浮点数在小端模式下的内存布局。该内存布局仅存储52位。 - Totonga

    2
    有多种不同的浮点数格式。它们中的大多数共享一些常见特征:一个符号位,一些用于存储指数的位和一些用于存储尾数(也称为有效数字)的位。
    IEEE浮点标准试图定义一种可以在各种系统上实现的单个格式(或几种大小的格式集)。它还定义了可用的操作及其语义。它已经非常流行,你可能遇到的大多数系统都使用IEEE浮点数。但是其他格式仍在使用,以及不完全符合IEEE标准的实现。C标准提供对IEEE的可选支持,但不强制要求。

    2

    尾数表示数字的最高有效位。

    指数表示需要对尾数进行多少次移位才能得到实际值。

    编码指定尾数符号和指数符号的表示方式(基本上是向左还是向右移位)。

    您所提到的文档指定了IEEE编码,这是最广泛使用的编码方式。


    2

    0

    它是实现定义的,虽然IEEE-754迄今为止是最常见的。

    要确保使用IEEE-754:

    • C语言中,使用#ifdef __STDC_IEC_559__
    • C++中,使用std::numeric_limits<float>::is_iec559常量

    我已经写了一些关于IEEE-754的指南,包括:


    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接