整型与浮点型
本教程共 100 篇 · 第 10 篇 · 更新于 2026-07-31 · 约 9 分钟阅读
10. 整型与浮点型
本节目标:分清各类整数类型和小数类型的范围与精度,记住”金额必须用 decimal”这条铁律。
数字是最常用的数据,但 C# 里”数字”分好几类。选错类型,轻则浪费内存,重则算错钱。这一章把整型和浮点型一次讲清。
整型(integral types)
整型(integral types)只装没有小数部分的数字。按”能装多大”分成几种:
| 类型 | 关键字 | 大致范围 | 占用 |
|---|---|---|---|
| 字节 | byte | 0 ~ 255 | 1 字节 |
| 短整型 | short | ±3 万左右 | 2 字节 |
| 整型 | int | ±21 亿左右 | 4 字节 |
| 长整型 | long | 极大(约 ±9×10¹⁸) | 8 字节 |
日常开发,int 是默认选择。人数、个数、序号用 int 足够。long 留给可能超 21 亿的场景,比如文件大小、时间戳毫秒。
namespace CSharpDemo;
int peopleCount = 100;
long fileSize = 9_000_000_000L; // 长整型字面量加 L 后缀
byte age = 18;
Tip数字中间的下划线
_是 C# 的数字分隔符,纯为提高可读性,编译时忽略。9_000_000_000和9000000000完全等价。
为什么 C# 要提供这么多档位?本质是在”省内存”和”够用”之间取舍。一个 byte 只占 1 字节,能装 0~255,适合表示颜色分量、单字节标志;而 int 占 4 字节却什么计数都够。对普通业务代码,内存不是瓶颈,int 最省心;只有在处理海量数据或二进制协议时,才值得为每字节精打细算。所以原则很明确:默认 int,超大用 long,只有在”必须用更小类型”的硬性场景才上 byte/short。
浮点型:带小数的数
需要小数时,用浮点型(floating-point types)。C# 有三个:float、double、decimal。它们的区别在精度和用途:
| 类型 | 关键字 | 精度 | 后缀 | 用途 |
|---|---|---|---|---|
| 单精度 | float | 约 7 位有效数字 | f | 对精度不敏感的科学计算 |
| 双精度 | double | 约 15–16 位 | d(可省) | 通用小数、图形、物理 |
| 精确小数 | decimal | 28–29 位有效十进制 | m | 金额、财务计算 |
这里”精度”指的是”能精确表示多少位有效数字”。float 约 7 位,意思是超过 7 位的数字它就开始存不准;double 约 1516 位,足够绝大多数科学测量;29 位十进制数字,专门为了把钱算得分毫不差。decimal 高达 28
为什么不只是用 double
double 看似万能,但它用二进制近似表示小数,某些十进制小数存不准:
namespace CSharpDemo;
double a = 0.1;
double b = 0.2;
Console.WriteLine(a + b); // 输出 0.30000000000000004
0.1 + 0.2 在 double 里不等于 0.3,而是多了个微小误差。对物理模拟无所谓,对”算账单”却是灾难——差一分钱财务都不答应。
为什么会这样?因为计算机底层用二进制存数,就像十进制没法精确写出 1/3(只能写 0.3333…),二进制也没法精确写出 0.1,只能存一个极接近的近似值。一次近似看不出,累加成千上万次后误差就被放大,账目就对不上了。decimal 改用十进制存储,正好绕开这个坑——它就是把你写的十进制小数原样保留。
金额必须用 decimal
decimal 用十进制表示,专门为金融设计,能精确存 0.1 这类数:
namespace CSharpDemo;
decimal price = 9.99m; // m 后缀不可省
decimal quantity = 3;
decimal total = price * quantity;
Console.WriteLine(total); // 29.97,精确
记住本教程的约定示例:decimal price = 9.99m。那个 m 告诉编译器”这是 decimal 不是 double”,漏写会报错或悄悄变成 double。
Note
decimal不是”无限精确”,只是它在十进制下表现正确,适合钱。float/double适合测量值(温度、长度),不适合计数和金额。
Warning这条是铁律,值得反复强调:凡是钱,全程
decimal,别让double混进来。 哪怕只是一处double参与计算,误差就会潜伏进结果,可能几天后才在月结对账时爆发,极难排查。常量也带m:const decimal Tax = 0.13m;。
一个踩坑现场
double d1 = 1.0 / 3.0; // 0.3333333333333333...
decimal m1 = 1.0m / 3.0m; // 0.3333333333333333333333333333
Console.WriteLine(d1 == 0.333333333333333m); // False,精度不同
比较两个小数是否相等也别直接用 ==,尤其 double,因为误差可能让”本该相等”的值不相等。金额比较常用”是否相差极小”或干脆用 decimal。
类型推断时的默认
用 var 时,小数字面量默认被推断成 double,整数推断成 int:
var x = 3.14; // double
var y = 3.14m; // decimal(因 m 后缀)
var z = 100; // int
所以写金额务必带上 m,否则 var 给你的是 double,又掉回精度坑。
整数溢出会怎样
int 有上限(约 21 亿)。若累加超过上限,默认情况下值会”绕回”成负数,而不是报错:
int max = int.MaxValue;
Console.WriteLine(max + 1); // 变成负数(溢出回绕)
这种”静默错误”很危险。如果你在做计数、编号这类不能出错的场景,可用 checked 块让溢出直接抛异常(详见第 12 章)。或者用更大的 long 兜底。
Note默认不检查溢出是为性能考虑——每次算术都查一遍会拖慢运行。但安全敏感处请用
checked显式开启,把隐患变明显。
Tip如果你担心某个累加逻辑会越界,最简单稳妥的办法是”从一开始就选够大的类型”。比如数量可能很大的场景直接用
long,比事后加checked更省心。
该用 float 还是 double
绝大多数情况用 double:它精度更高,且是 var 推断小数字面量的默认。只有在两种特殊场景才用 float:
- 和强制要求
float的旧库/图形 API 对接。 - 海量数值且内存极敏感(如百万级浮点数组),
float省一半内存。
日常业务代码,别为了”省内存”盲目用 float,精度损失往往更贵。
整数相除想留小数怎么办
这是新手高频坑:两个 int 相除,C# 会做整数除法,直接丢弃小数部分。
int a = 5;
int b = 2;
Console.WriteLine(a / b); // 输出 2,小数被丢
double result = (double)a / b; // 2.5,先把一边转成 double
Console.WriteLine(result);
要保留小数,至少让参与运算的一边是小数类型。1.0 / 3 或 (double)a / b 都行。记住一句话:要小数,先有小数字面量。
Warning整数除法”静默丢小数”不会报错,结果看着还挺正常,所以特别容易埋坑。凡是做除法且希望有小数,先确认操作数里有小数类型,否则算出来的”平均值""比率”可能整型截断,逻辑全错。
常见疑问解答
问:byte、short 这么小,平时用得到吗?
偶尔。处理二进制协议、文件格式、网络包时,常按字节精确布局,这时 byte 是刚需。日常业务计数几乎都用 int,不必刻意用小类型”省内存”。
问:double 和 decimal 能混着算吗?
可以,但会发生隐式转换,且方向是 double 转 decimal 需显式。最佳实践是全链路统一:金额一律 decimal,测量值用 double,别让两者在表达式里相遇,免得精度规则打架。
问:整数相除想保留小数怎么办?
至少让一边是小数类型:1.0 / 3 或 (double)a / b。若两边都是 int,C# 做整数除法直接丢小数。这是新手高频坑,记住”要小数,先有小数字面量”。
问:decimal 那么精确,是不是所有小数都用它?
不必。decimal 占用 16 字节、运算比 double 慢,且不适合科学/图形这类超大范围或需极小绝对误差的场景。它的定位就是”财务精确”。测量值、坐标、物理量仍用 double,各司其职。
要点速记
int是默认整数;大数用long;小范围用byte/short。double通用小数;float仅特殊场景。- 金额必须用
decimal且带m后缀。 double有二进制误差,别拿它算钱或比相等。- 两个整数相除会丢小数,要小数先有小数字面量。
动手练一练
- 用
decimal表示三件商品单价9.99m、19.5m、3.0m,计算总价并打印,体会金额用decimal的必要。 - 写
double a = 0.1 + 0.2;,打印结果,再写decimal b = 0.1m + 0.2m;,对比两者差异并解释。 - 用
long存一个超过 21 亿的数字(如9_000_000_000L),确认int装不下而long可以。
浮点比较的工程做法
既然 double 有误差,工程上比较两个小数”是否相等”不用 ==,而是看”差的绝对值是否极小”:
double a = 0.1 + 0.2;
double b = 0.3;
bool equal = Math.Abs(a - b) < 1e-9; // 差小于十亿分之一算相等
Console.WriteLine(equal);
Math.Abs 取绝对值,1e-9 是科学计数法表示的极小量。这叫”容差比较”,是数值计算的基本功。当然,最省心的还是用 decimal 处理金额,从根上避开误差。
选类型的速查清单
记不住细节时,用这张表快速决策:
- 计数、编号、年龄、数量 →
int,超大用long。 - 温度、长度、物理测量、图形坐标 →
double。 - 单价、金额、税率、余额 →
decimal(带m)。 - 二进制协议、单字节标志 →
byte。
Tip一个实战原则:金额字段从数据库到界面全程
decimal,别在中间转成double算完再转回。转换一次就可能引入误差,且难以排查。
小结与下一步
整数用 int(大数用 long);小数通用 double,但钱一律 decimal 加 m。精度不是小事,选对类型,账目才不会差那一分。整数相除别忘丢小数,溢出要用 checked 或更大类型兜底。
下一章看字符与布尔:char 和 bool 这两个看似简单却很重要的小类型。