🐾 个人主页 🐾
阿松爱睡觉,横竖醒不来 🏅你可以不屠龙,但不能不磨剑🗡
目录
- 一、前景概要
- 二、情景再现
- 三、顺藤摸瓜
- 四、水落石出
- 五、文末寄语
一、前景概要
上周的工作任务是写了一个关于账单列表,和同事写完之后测试发现,在余额提现的时候会莫名奇妙的少了一分钱,但是前期刚开始测试的时候不会出现类似的问题,0.11也可以正常提现。但是随着测试时间的流逝,问题也就随之出现了——提现的时候会少一分钱,提现0.11,只给0.10。
那还得了,关于财务的问题,但凡少一分钱那也是大事,更何况是提现的系统出错。出现问题之后就找问题出现的根源,最后发现是 float
类型用错了,且听我慢慢讲解。
二、情景再现
我们先了解一下功能需求和代码实现。
需求:就是获取提现账单,查看数据,类似下面这样,没什么复杂的。
时间 | 收支 | 明细 | 余额(元) |
2024.12.13 | -0.11 | 提现 | 2499.89 |
2024.12.13 | -500 | 提现 | 2500.00 |
2024.12.13 | +1000 | 佣金 | 3000.00 |
2024.12.13 | +2000 | 佣金 | 2000.00 |
实现:实现也比较简单,就是把接收到的数据显示在前端就行,但问题就出在显示了,先看下大致代码:
//接收到的数据格式
{
"time":"2024.12.10",
"incoming_and_outgoings":"-1000",
"detail":"提现",
"balance":300000,//后端数据的单位是"分"
}
Text1.text = data["time"];
Text2.text = data["incoming_and_outgoings"];
Text3.text = data["detail"];
Text4.text = (data["balance"] / 100f).ToString("F2");//前端的单位是“元”,后端数据的单位是“分”
最终显示在界面上的效果的时候出了问题,大致的界面数据显示是这样的:
时间 | 收支 | 明细 | 余额(元) |
2024.12.18 | -0.11 | 提现 | 202499.90 |
2024.12.17 | +200000 | 佣金 | 202500.00 |
2024.12.16 | -500 | 提现 | 2500.00 |
2024.12.15 | -0.21 | 提现 | 3000.00 |
2024.12.14 | +500.21 | 佣金 | 3000.21 |
2024.12.13 | +2500 | 佣金 | 2500.00 |
仔细看数据你就会发现,最新的2024.12.18
那一条数据对不上了,里外少了0.01
。但是最开始2024.12.14
那一条的数据就没事。这是什么原因?奇了怪了。
三、顺藤摸瓜
于是打印接收到的后端数据,发现数据并没有问题,最新一条的数据就是202499.89
,那就是在数字转字符串的时候出现的问题。
Text4.text = (data["balance"] / 100f).ToString("F2");
但是看代码看半天也没有发现什么问题,于是就猜测:有的会发生有的不会发生,偶然性的?是不是精度缺失导致的🤔?但根据多年来的使用经验来说,精度缺失应该不太会,毕竟是float
类型,我一直是按照0.01的精度去使用float
的,虽然没有double
精度高,但也不至于说小数点后两位都搞不准吧。那换成double
类型试试?
Text4.text = (data["balance"] / 100d).ToString("F2");//把 100f 换成 100d
运行测试,好了!!!数据准确无误的显示在界面上,出错地方的数据显示成了 202499.89
,这下解决了,多测试了几个数据也是没有问题的。
四、水落石出
难道真的是 float
类型出的问题?上网一顿搜索,问了一下AI, float
的精度到底怎么个事?结果如下:
float
类型在 C# 中表示单精度 32 位 IEEE 754 浮点数。它的精度大约是 7 到 8 位十进制数字,这意味着它能精确表示的数字范围大约在这个精度内。
以下是float
类型的一些关键特性:
- 大约可以精确表示 7 到 8 位十进制数。
- 指数范围大约从 -38 到 +38。
- 最小非零值大约是 1.4 x 10^-45。
- 最大值大约是 3.4 x 10^38。
由于
float
是浮点数,它使用科学记数法来表示非常大或非常小的数值,这意味着在超出其精确表示范围时,它会开始丢失精度。例如,当你尝试表示一个非常精确的十进制小数时,float
可能无法精确表示,因为它内部是以二进制形式存储的,而某些十进制小数无法在二进制浮点数中精确表示。
可以发现 float
的精度之后7-8位十进制的精度,那就合理了。因为后端传过来的数据单位是 分
,所以整体就超过了8位数。所以在除以100之后,精度缺失,就少了一分钱!
五、文末寄语
大家在开发的时候,对于数值之间的转换一定要注意,无论是数字转字符串,还是 int
、float
、double
类型之间的相互转换,都要注意他们之间的精度。所以大家在做对精度有要求的数值转换的时候,用 double
类型来做。
大家看个示例:
当8、9位数的时候,精度就缺失了,八位数 80000031/100f
少了0.01,九位数 200000031/100f
直接少了0.31,但是五六七位数就没事,所以可见 float
的精度就只有七位,大于七位的精度直接缺失。另外再看下 double
类型的示例:
到了11位数依旧是稳如老狗啊,我们再看下 double
类型定义和特性:
在 C# 中,
double
类型是 IEEE 754 双精度浮点数,它通常有 15 到 17 位十进制数的精度。这意味着double
类型可以精确表示大约 15 到 17 位数字,但超过这个范围的数字可能不会完全精确。
以下是double
类型的关键特性:
- 大约可以精确表示 15 到 17 位十进制数。
- 拥有大约 15 到 17 位十进制数的精度。
- 大小范围大约从 ±5.0 × 10^-324 到 ±1.7 × 10^308。
需要注意的是,
double
类型的精度并不是固定的,因为浮点数在表示非常大或非常小的数值时,尾数的有效数字位数会减少。例如,非常大或非常小的数值可能只能保证少数几位数的精度。
除了 double
还有比它更高的精度的类型,就是: decimal
类型,我们再看下它的特性:
在 C# 中,
decimal
类型是一种高精度的十进制数,它提供了固定的 28 位小数点精度(也称为尾数精度)加上一个符号位。这意味着decimal
可以精确表示 28 到 29 位十进制数(包括整数部分和小数部分)。
以下是decimal
类型的一些关键特性:
- 精度:28 到 29 位十进制数。
- 范围:大约从 ±1.0 × 10^-28 到 ±7.9 × 10^28。
由于
decimal
类型使用的是固定的小数点精度,它非常适合于金融和货币计算,因为在这些领域中,精确的数值表示非常重要。与double
类型相比,decimal
类型牺牲了数值范围以换取更高的精度。
在用的时候显示指定用 m
或 M
,像这样:
decimal decimalNumber = 1000.00m;
所以大家根据自己的开发需求选择合适的类型。
还有一点,就是大家对于数字类型定义的属性一定要清晰,就比如我一直拿 float
类型当 小数点后两位的数,即 xxx.00
来使用,所以多多少少会给开发埋雷。
这篇的内容就到此结束了,下期见,拜拜!