- 浮点数
- 定点数
下面我们详细解释这两种形式。

(图片来源网络,侵删)
浮点数
这是现代C语言中最常用、最标准的表示实数的方式,它的设计灵感来源于科学记数法,即一个数可以表示为 尾数 × 基数^指数 的形式。
a) 核心思想
计算机将实数的存储空间分为三个部分:
- 符号位:1位,表示正负。
- 指数位:若干位,表示小数点的位置,决定了数值的表示范围。
- 尾数位:剩余的位,表示有效数字,决定了数值的精度。
这种“浮动”小数点的机制使得浮点数既能表示极大(如天体间的距离)也能表示极小(如原子质量)的数值,但精度会随着数值大小变化。
b) C语言中的类型
C语言通过三种关键字来提供不同精度的浮点类型:

(图片来源网络,侵删)
| 类型 | 别名 | 通常占用字节 | 有效数字位数 | 精度说明 |
|---|---|---|---|---|
float |
单精度浮点数 | 4 字节 (32位) | 约 6-7 位 | 精度较低,适用于对精度要求不高的场景,如游戏中的坐标、图形学计算。 |
double |
双精度浮点数 | 8 字节 (64位) | 约 15-16 位 | 默认使用,精度足够高,适用于绝大多数科学计算、工程计算和金融计算。 |
long double |
长双精度浮点数 | 8, 12, 或 16 字节 | ≥ 15 位 | 提供比 double 更高的精度,具体实现依赖于编译器和平台。 |
c) 示例代码
#include <stdio.h>
int main() {
float f = 3.1415926535f; // 注意 f 后缀,表示这是一个 float 常量
double d = 3.141592653589793;
printf("float f: %.10f\n", f); // 输出: float f: 3.1415925026
printf("double d: %.15f\n", d); // 输出: double d: 3.141592653589793
// 可以看到,float 在存储时丢失了精度,而 double 保留了更多有效数字。
return 0;
}
d) 优点
- 范围广:能表示非常大和非常小的数值。
- 标准化:有IEEE 754等国际标准,保证了不同平台下计算结果的一致性。
- 硬件支持:现代CPU都有专门的浮点运算单元,计算速度很快。
e) 缺点
- 精度有限:由于存储空间有限,浮点数无法精确表示所有十进制小数(
1在二进制浮点数中是一个无限循环小数,只能近似存储),这会导致精度误差。 - 存在特殊值:如
NaN(Not a Number) 和Infinity,需要小心处理。
定点数
定点数是一种更简单、更直接的表示方法,它不使用指数,而是预先约定好小数点的位置。
a) 核心思想
一个定点数被看作一个整数,程序员需要自己记住或通过数据结构(如结构体)来跟踪小数点的位置,我们可以用一个32位的整数来表示金额,并约定小数点后有2位数字。
- 例子:用
int类型存储12345,我们约定小数点后有2位,那么它实际代表的值是45。
b) C语言中的实现
C语言没有内置的定点数类型,实现定点数通常有两种方式:
- 使用整数类型模拟:直接使用
int,long,int64_t等整数类型,程序员通过编程约定来管理小数点。 - 使用自定义结构体:将整数部分和小数部分封装在一个结构体中,这样更清晰,但效率较低。
c) 示例代码 (使用整数模拟)
#include <stdio.h>
#include <stdint.h> // 用于 int64_t
// 假设我们使用 int64_t,并约定小数点后有4位精度
typedef int64_t fixed_t;
// 将浮点数转换为定点数
#define FLOAT_TO_FIXED(f) ((fixed_t)((f) * 10000))
// 将定点数转换为浮点数
#define FIXED_TO_FLOAT(f) ((double)(f) / 10000.0)
// 定点数加法
fixed_t fixed_add(fixed_t a, fixed_t b) {
return a + b;
}
int main() {
fixed_t price = FLOAT_TO_FIXED(19.95); // 19.95 * 10000 = 199500
fixed_t tax = FLOAT_TO_FIXED(1.25); // 1.25 * 10000 = 12500
fixed_t total_price = fixed_add(price, tax); // 199500 + 12500 = 212000
double total_float = FIXED_TO_FLOAT(total_price); // 212000 / 10000.0 = 21.2
printf("Price (fixed): %lld\n", price); // 输出: Price (fixed): 199500
printf("Tax (fixed): %lld\n", tax); // 输出: Tax (fixed): 12500
printf("Total (float): %.2f\n", total_float); // 输出: Total (float): 21.20
return 0;
}
d) 优点
- 精确表示:只要小数部分能被约定的基数(如10、100)整除,就可以精确表示,非常适合金融计算(如货币)。
- 计算速度快:定点数的运算本质上是整数运算,在某些没有强大FPU的嵌入式系统中,比浮点数运算更快。
- 可预测性:没有浮点数那样的舍入误差累积问题。
e) 缺点
- 范围受限:由于没有指数,表示的范围远小于浮点数,容易发生溢出。
- 使用复杂:程序员需要手动处理小数点的转换和运算,容易出错。
- 不通用:没有统一的硬件支持,所有运算都需要软件模拟(除非使用特殊的DSP指令)。
总结与对比
| 特性 | 浮点数 | 定点数 |
|---|---|---|
| 底层原理 | 科学记数法(符号、指数、尾数) | 固定小数点位置 |
| C语言支持 | 内置类型 (float, double, long double) |
无内置类型,需用整数或结构体模拟 |
| 精度 | 有限精度,存在舍入误差 | 可精确表示特定小数(如0.1, 0.01) |
| 数值范围 | 范围极广,从极小到极大 | 范围受限,容易溢出 |
| 运算速度 | 现代CPU上非常快(有FPU支持) | 在无FPU的嵌入式系统中可能更快,否则较慢 |
| 主要用途 | 科学计算、图形学、机器学习、通用计算 | 金融计算(货币)、嵌入式系统、对精度要求苛刻的领域 |
| 编程复杂度 | 低,直接使用即可 | 高,需要手动管理小数点,容易出错 |
如何选择?
- 优先选择
double:在绝大多数情况下,直接使用double是最佳选择,它提供了足够的精度和范围,并且性能优异。 - 在特定场景下考虑定点数:
- 当你处理的业务数据必须精确(例如货币计算,不能有
1 + 0.2 != 0.3的情况)。 - 当你工作在资源受限的嵌入式系统,且该系统没有硬件浮点单元,而你的应用对性能要求很高。
- 当你处理的数值范围固定且不会溢出,并且需要完全避免浮点数的舍入误差。
- 当你处理的业务数据必须精确(例如货币计算,不能有
浮点数是C语言中表示实数的“标准武器”,而定点数则是一种在特定需求下(尤其是精度和性能权衡)的“特殊战术”。
