“Double”并非为两位小数而生

87 阅读4分钟

前提:偶然发现double类型可以存储超过两位小数!!!

image.png

我一直持有 double 类型仅能存储两位小数的误解,直到偶然发现事实并非如此。通过查证,我才恍然大悟,并修正了这一根深蒂固的错误观念。

Double数据类型定义:双精度浮点数

一、浮点数

浮点数:指的是一种用来表示实数(即包含小数点的数)的计算机数据类型。它的核心思想是“浮动的小数点”。

1. 类比:科学计数法

为了更好地理解,我们可以回想一下数学中的科学计数法。例如:

  • 光速大约是 300,000,000 米/秒,可以写成 3.0 × 10⁸
  • 一个电子的质量大约是 0.0000000000000000000000000009109 千克,可以写成 9.109 × 10⁻²⁸

你发现了什么?无论数字多大或多小,我们都可以通过移动小数点(即“浮动”小数点)的位置,将其表示为一个 有效数字 和一个 10的幂次 的乘积

2. 在计算机中的实现

计算机中的浮点数原理完全相同,只不过它使用的是二进制(以2为底)而不是十进制(以10为底)。

一个浮点数在计算机内部通常由三部分组成(遵循IEEE 754标准):

  • 符号位:决定这个数是正数还是负数。
  • 指数位:决定这个数的“规模”或“范围”(相当于科学计数法中的幂次部分)。
  • 尾数位:决定这个数的“精度”(相当于科学计数法中的有效数字部分)。

所以,“浮点数”就是一种用类似科学计数法的方式,在计算机中表示实数的方法,其小数点位置可以根据数值的大小而“浮动”。

二、双精度

“双精度”指的是这种浮点数在计算机内存中所占用的比特位数更多,从而具有更高的精度和更大的表示范围

1. 精度的对比

在IEEE 754标准中,最常见的两种浮点数是:

  • 单精度浮点数:占用 32位(4字节)

    • 符号位:1位
    • 指数位:8位
    • 尾数位:23位
    • 通常我们在编程中称之为 float
  • 双精度浮点数:占用 64位(8字节)

    • 符号位:1位
    • 指数位:11位
    • 尾数位:52位
    • 通常我们在编程中称之为 double

2. “双”是什么意思?

“双精度”这个名字非常直观,因为它占用的位数(64位)正好是单精度(32位)的两倍。更多的位数带来了两大好处:

  • 更高的精度:尾数位从23位增加到52位,意味着它可以表示更多的小数点后的有效数字。这使得计算结果更精确,累积误差更小。
  • 更大的范围:指数位从8位增加到11位,意味着它可以表示更大和更小的数字。

3. 举例说明

假设我们要存储圆周率 π:

  • 单精度 float 可能只能存储为:3.1415927
  • 双精度 double 则可以存储为:3.141592653589793

你可以清楚地看到,双精度能够保留更多的小数位,因此更精确。

  • Float(单精度) :提供约6-9位有效十进制数字
  • Double(双精度) :提供约15-17位有效十进制数字

三、二进制表示法

计算机内部使用二进制(只有0和1)来存储所有数据,包括数字。

我们来看看 0.1 和 0.2 在二进制中是什么样子:

  • 十进制 0.1 转换成二进制

    • 二进制小数位的权重是 2⁻¹ (0.5), 2⁻² (0.25), 2⁻³ (0.125), 2⁻⁴ (0.0625) ...

    • 尝试组合:0.1 = ?

      • 0.0625 (2⁻⁴) 太小
      • 0.125 (2⁻³) 太大
      • 实际上,0.1 的二进制表示是:0.000110011001100110011001100110011...
    • 看到了吗?这是一个 无限循环二进制小数

  • 十进制 0.2 转换成二进制

    • 0.2 是 0.1 的两倍,所以在二进制中,它就是上面那个循环小数左移一位:
    • 0.00110011001100110011001100110011...
    • 同样也是一个 无限循环二进制小数

现在,当你执行 0.1 + 0.2 时:

  1. 计算机取出它存储的 0.1 的近似值。
  2. 取出它存储的 0.2 的近似值。
  3. 将这两个近似值相加。
  4. 得到的结果,自然也是这两个近似值之和的近似值。

这个最终结果,在二进制中可能又是另一个需要舍入的数字。当计算机把这个二进制结果转换回我们熟悉的十进制显示给你看时,就出现了那个著名的 0.30000000000000004