浮点数的数据格式


Vincent
发布于 2024-08-03 / 36 阅读 / 0 评论 /
浮点数的数据格式 根据 IEEE754 的标准定义:浮点数的数据格式可以分为最常见的单精度(32位)和双精度(64位)格式,以及半精度(16位)、四精度(128位)和扩展精度格式。 单精度浮点数(Single-Precision Floating-Point):在计算机编程中常用float表示: 通

浮点数的数据格式

根据 IEEE754 的标准定义:浮点数的数据格式可以分为最常见的单精度(32位)和双精度(64位)格式,以及半精度(16位)、四精度(128位)和扩展精度格式。

单精度浮点数(Single-Precision Floating-Point):在计算机编程中常用float表示:

通常占用 32 位(4 个字节)的存储空间

在IEEE 754标准中,单精度浮点数由1位符号位、8位指数位和23位尾数位组成。

一个 4 个字节(32 位)的单精度浮点数可以表示大约 ±3.4 x 10^38 的范围内的小数。这个范围非常大,可以满足大部分应用程序对小数值的存储需求。同时,单精度浮点数通常提供6-7 位有效数字的精度。

双精度浮点数(Double-Precision Floating-Point):在计算机编程中常用double表示,如:

//定义一个圆形类,其中包括了圆周率常量和半径变量
package com.buptnu.demo;

public class CCircle {

final double PI = 3.141592;

double radius;

}

  • 通常占用 64 位(8 个字节)的存储空间

  • 在IEEE 754标准中,双精度浮点数由1位符号位、11位指数位和52位尾数位组成。

  • 一个 8 个字节(64 位)的双精度浮点数可以表示大约 ±1.7 x 10^308 的范围内的小数。这个范围非常大,可以满足大部分应用程序对小数值的存储需求。同时,双精度浮点数通常提供大约 15-16 位有效数字的精度

注:-6.02×10^23 这个数,“-"表示符号位,用于表示正负,6.02表示尾数,10^23表示指数

在程序设计和科学计算中,单精度浮点数通常用于那些不需要极高精度计算的场合,因为与双精度浮点数相比,单精度浮点数在计算速度和占用存储空间方面具有一定的优势。然而,当需要更高精度和更大范围的数值时,通常会使用双精度浮点数(64位),尽管这会以牺牲一定的性能和增加存储需求为代价

其余三种数据格式

扩展精度浮点数(Extended Precision):

此类别下的具体格式可能会根据不同的硬件和软件实现有所变化。通常,它们提供比双精度更高的精度和/或更广的范围。

例如,在某些实现中,扩展精度可能使用80位(10字节)或更多位来表示一个浮点数。

半精度浮点数(Half Precision):

使用16位(2字节)来表示一个浮点数。

包括1位符号位、5位指数位和10位尾数位。

半精度格式主要用于特定的应用领域,如图形处理和深度学习,其中计算效率和存储效率可能比数值精度更加重要。

四精度浮点数(Quadruple Precision):

使用128位(16字节)来表示一个浮点数。

包括1位符号位、15位指数位和112位尾数位。

四精度提供了极高的数值精度,适用于科学计算中需要极高精度的计算场景。

IEEE 754标准的这些不同格式使得浮点数在各种计算需求中具有灵活性,从而可以根据具体的应用场景选择最合适的数据精度和大小。

注意:上面提到的是属于不同的的数据格式,并不是浮点数的分类,是用于表示各种类型的浮点数,包括规约数、非规约数、零、无穷大和NaN

根据IEEE 754标准,浮点数主要可以被分为以下几类:

规约数(Normalized numbers):

这是最常见的浮点数类型,用于表示除零以外的大多数数值。

规约数的表示确保了尾数(mantissa)的最高位隐含为1(对于二进制浮点数而言)。这意味着实际的尾数比存储的尾数多一个最高位的1,从而提高了表示的有效性。

指数部分既可以是正数也可以是负数,通过一个偏移量(或称为偏置)来表示。

非规约数(Denormalized numbers,也称为次正规数):

这类浮点数主要用于表示非常接近于0的正数和负数。

非规约数的指数固定为最小值,而尾数不假定隐含的最高位为1,这允许表示比规约数更接近于零的数值。

主要作用是提供了一种平滑的过渡方式,使得数值可以渐进到0,避免了硬阈值导致的突变,这一点在数值计算中非常重要。

零(Zero):

IEEE 754标准定义了正零和负零,尽管在大多数运算中它们的行为是相同的,但在某些情况下,区分正负零可以提供更多的数学灵活性(比如在处理极限或导数时)。

=无穷大(Infinity):

分为正无穷大和负无穷大,用于表示超出浮点数表示范围的大数值,比如溢出的结果。

在数学运算中,无穷大的引入允许某些运算可以继续进行,而不是简单地失败。

NaN(Not a Number,不是一个数字):

用于表示那些无法定义或不适用于常规浮点数的结果,如0除以0、无穷大减去无穷大等。

NaN本身有多种形式,可以携带额外信息来指示导致NaN结果的特定原因。

这些分类使得IEEE 754标准的浮点数能够灵活地表示和处理广泛的数值,包括非常特殊的情况,同时在各种计算场景中保持数学上的一致性和可预测性。

半精度是英伟达在2002年搞出来的,双精度和单精度是为了计算,而半精度更多是为了降低数据传输和存储成本。

很多场景对于精度要求也没那么高,例如分布式深度学习里面,如果用半精度的话,比起单精度来可以节省一半传输成本。考虑到深度学习的模型可能会有几亿个参数,使用半精度传输还是非常有价值的。

Google的TensorFlow就是使用了16位的浮点数,不过他们用的不是英伟达提出的那个标准,而是直接把32位的浮点数小数部分截了。据说是为了less computation expensive。。。

比较下几种浮点数的layout:

  • 双精度浮点数:

  • 单精度浮点数:

  • 半精度浮点数:

它们都分成3部分,符号位,指数和尾数。不同精度只不过是指数位和尾数位的长度不一样。

解析一个浮点数就5条规则:

  1. 如果指数位全零,尾数位是全零,那就表示0

  2. 如果指数位全零,尾数位是非零,就表示一个很小的数(subnormal),计算方式 (−1)^signbit × 2^−126 × 0.fractionbits

  3. 如果指数位全是1,尾数位是全零,表示正负无穷

  4. 如果指数位全是1,尾数位是非零,表示不是一个数NAN

  5. 剩下的计算方式为 (−1)^signbit × 2^(exponentbits−127) × 1.fractionbits 常用的语言几乎都不提供半精度的浮点数,这时候需要我们自己转化。

具体可以参考Numpy里面的代码:

https://github.com/numpy/numpy/blob/master/numpy/core/src/npymath/halffloat.c#L466

当然按照TensorFlow那么玩的话就很简单了(~摊手~)。

参考资料:

https://en.wikipedia.org/wiki/Half-precision_floating-point_format

https://en.wikipedia.org/wiki/Double-precision_floating-point_format

https://en.wikipedia.org/wiki/Single-precision_floating-point_format

http://download.tensorflow.org/paper/whitepaper2015.pdf