float和double的爱恨情仇

float和double的爱恨情仇

特性 float (单精度) double (双精度)
内存占用 4 字节 (32 bit) 8 字节 (64 bit)
有效数字 约 6 ~ 7 位 约 15 ~ 16 位
精度评价 够用,但容易有误差 非常准,默认推荐
C语言写法 float x = 1.23f; double x = 1.23;
输入/输出 %f %lf (输入时必须用, 输出可用%f)

一、为什么是“约 6~7 位”?

C 语言很严谨,但“二进制”和“十进制”无法完美互译

底层逻辑:

  • float 在计算机里有 23 个二进制位 用来存有效数字。
  • 2 的 23 次方是 8,388,608
  • 你看,838万 这是一个 7 位数。这意味着 float 能把大部分 7 位以内的整数分得清清楚楚。
  • 但是,一旦超过这个范围,或者涉及到某些特定的小数,二进制位就不够用了。

比喻: 这就好比用“美元”去换“人民币”。

  • 你有 1.00 美元。
  • 汇率算下来可能是 7.234567… 元。
  • 你能精确对应的只有前几位,后面总是会有换算产生的“毛边”。

所以,教科书严谨地说“约 6~7 位”,意思就是:“前 6 位十进制数我是敢打包票准的,第 7 位能不能准看运气,第 8 位以后就是瞎蒙了。”

二、int,float,double 的大小比较

1. 比“物理体重” (占用内存大小)

这是指它们在计算机内存条里占了几个小格子(字节)。

  • int:通常 4 字节 (32 bit)
  • float4 字节 (32 bit)
  • double8 字节 (64 bit)

结论: double > float = int

(注:float 和 int 虽然类型不同,但在现代 PC 上占用的空间通常是一样大的!)

2. 比“肚量” (数值范围)

这是指它们最大能存多大的数。这里有巨大的反转

  • int (4字节):
    • 最大约 21 亿 ($2 \times 10^9$)
    • 超过这个数就会溢出(变成负数)。
  • float (4字节):
    • 最大约 $3.4 \times 10^{38}$
    • 这是 34 后面跟 37 个零!是一个天文数字。
  • double (8字节):
    • 最大约 $1.7 \times 10^{308}$
    • 这是一个宇宙级别的数字,宇宙原子总数才 $10^{80}$ 左右。

结论: double >>> float >>> int

3. 灵魂发问:为什么 intfloat 都是 4 字节,float 却能装那么多?

这就像同样大的两个箱子(4字节):

  • int 箱子: 实打实地把每个豆子都装进去。装满了就是 21 亿颗。
  • float 箱子: 它耍了小聪明(科学计数法)。它只装“大概数量”和“后面有几个零”。
    • 比如存 2000...000,它只存 2指数

代价是什么?

  • int 虽然存的小,但是一颗都不差,精确度 100%。
  • float 虽然存的大,但是有空隙,精度低

三、float精度低是什么意思?

想象你有两把尺子:

  • int (整数) 的尺子:

    这是一把极其精密的短尺子。上面的刻度是 1, 2, 3, 4… 每一个整数刻度都有。

    • 优点: 只要在范围内,多少就是多少,绝不会错。
    • 缺点: 尺子比较短(只能量到21亿)。
  • float (浮点数) 的尺子:

    这是一把可以伸缩的橡皮尺子。

    • 当你量小物体的时(数字小): 橡皮筋没拉开,刻度很密,可以精确到 0.000001
    • 当你量大物体时(数字大): 橡皮筋被狠狠拉长了

    关键点来了:

    当数字非常大时(比如 $10^{20}$),尺子上的刻度被拉得非常稀疏。刻度之间出现了巨大的空隙!

    可能上一个刻度是 100000000000000000000。

    下一个刻度直接就是 100000000000000000005。

    中间的 …001, …002, …003 哪里去了?

    因为尺子刻度不够密,这些数存不进去! 它们掉进了“精度的缝隙”里。如果你非要存 …003,它只能被迫吸附到最近的刻度 …000 或者 …005 上。

代码实测(震撼一下):

这叫“大数吃小数”现象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>

int main() {
// 定义一个 float,给它一个超级大的数(2000万)
float a = 20000000.0f;

// 给它加 1
float b = a + 1.0f;

// 打印看看
printf("a = %f\n", a);
printf("b = %f\n", b);

if (a == b) {
printf("恐怖的事情发生了:a 竟然等于 b!\n");
} else {
printf("a 不等于 b,正常。\n");
}

return 0;
}

如果运行了,你会发现:

在 float 的世界里,2000万 + 1 依然等于 2000万。

那个 1 因为太小,掉进大数刻度的缝隙里,直接被忽略了。

这就是“精度低”的含义:

虽然我也能存大数,但我存了大数,就看不清小数了。我想存“银河系的直径(大数)+ 1毫米(小数)”,float 会直接把那 1 毫米扔掉,因为它记不住那么精细的差别。

而 double 呢?

double 的尺子刻度比 float 密得多。在这个例子里,用 double 就能清晰地分辨出 20000000 和 20000001。

同理,double在非常大的时候也会出现精度问题~

四、给float赋值的时候一定要加f吗?

语法上不是必须的,但为了“资深”和“性能”,强烈建议加

原因: 在 C 语言里,任何写在代码里的“裸”小数(比如 123.456),默认被当作 double(双精度) 处理。

如果你写 float x = 123.456;

  1. 编译器先在内存里生成一个巨大的 8 字节 double123.456
  2. 然后发现你要把它塞进一个 4 字节的 float 变量里。
  3. 编译器不得不执行一次“截断”操作(砍掉一半精度),再赋值过去。

如果你写 float x = 123.456f;

  1. 编译器一看有个 f,直接生成一个 4 字节的 float 值。
  2. 直接塞进变量,干净利落。

资深程序员怎么看?

  • 不加 f 可能会触发编译警告(Warning: conversion from ‘double’ to ‘float’ may alter its value)。
  • 消灭 Warning 是资深程序员的洁癖。所以,加上 f 是好习惯。

五、printf打印出来是几位?

不管你的变量在肚子里(内存里)有多精确,也不管你是 float 还是 double,只要你使用了 %f(或 %lf)且不指定位数printf 就会机械地执行命令:“给我凑够 6 位小数打印出来!”

番外

%g 是 C 语言中最“聪明”、最“懂事”的打印格式。

你可以把它理解为 General(通用) 格式,或者 Smart(智能) 格式。它的核心逻辑是:“怎么短,怎么打印;怎么方便阅读,怎么打印。”

它自动在 %f(普通小数)和 %e(科学计数法)之间反复横跳,只为你提供最清晰的显示结果。


1. %g 的两大神技

神技一:自动去尾(强迫症福音)

这是大家最爱它的地方。

  • 如果你用 %f 打印 1.5,它非要打印 1.500000
  • 如果你用 %g 打印 1.5,它就打印 1.5。它会自动把后面没用的 0 全删掉,小数点如果没用也会删掉。

神技二:自动切换科学计数法

当数字特别大或者特别小(比如小于 0.0001)时,%f 会变得很丑或者显示不出精度,这时候 %g 会自动变身。

举例对比:

你的数字 (double) %f (死板模式) %g (智能模式) 评价
12.5 12.500000 12.5 %g 帮你把废 0 删了,清爽!
100.0 100.000000 100 %g 连小数点都帮你省了。
0.00000123 0.000001 1.23e-06 %f 精度丢了,%g 自动切成科学计数法,保住了精度。
123456789000 123456789000.000000 1.23457e+11 数字太大,%g 觉得你数不过来 0,帮你切成科学计数法。

2. 什么时候 不要%g

虽然它很聪明,但有时候我们需要“笨”一点:

❌ 场景一:需要对齐打印报表时

如果你要打印一列账单或数据表:

Plaintext

1
2
3
12.5
3.14159
100

%g 打印出来的长度参差不齐,小数点忽左忽右,非常难看。 这种时候必须用 %f(甚至 %.2f,强制大家都是两位小数,整整齐齐。

❌ 场景二:金钱相关

显示金额时,通常习惯强制保留两位小数(比如 ¥10.00),而不是 ¥10。这时候用 %.2f 更符合人类习惯。


总结

  • %f:老实人。由于太死板(由于默认 6 位),适合做排版对齐
  • %g:聪明人。适合看数据、调试、日常显示,因为它会自动根据数字的大小“看人下菜碟”,怎么舒服怎么来。

float和double的爱恨情仇
http://example.com/2025/12/24/float和double的爱恨情仇/
作者
王柏森
发布于
2025年12月24日
许可协议