消失的一分钱_程序人生


🐾 个人主页 🐾


阿松爱睡觉,横竖醒不来 🏅你可以不屠龙,但不能不磨剑🗡



目录

  • 一、前景概要
  • 二、情景再现
  • 三、顺藤摸瓜
  • 四、水落石出
  • 五、文末寄语


一、前景概要

上周的工作任务是写了一个关于账单列表,和同事写完之后测试发现,在余额提现的时候会莫名奇妙的少了一分钱,但是前期刚开始测试的时候不会出现类似的问题,0.11也可以正常提现。但是随着测试时间的流逝,问题也就随之出现了——提现的时候会少一分钱,提现0.11,只给0.10。

消失的一分钱_十进制数_02

那还得了,关于财务的问题,但凡少一分钱那也是大事,更何况是提现的系统出错。出现问题之后就找问题出现的根源,最后发现是 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那一条的数据就没事。这是什么原因?奇了怪了。

消失的一分钱_数据_03

三、顺藤摸瓜

于是打印接收到的后端数据,发现数据并没有问题,最新一条的数据就是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之后,精度缺失,就少了一分钱!

消失的一分钱_十进制数_04

五、文末寄语

大家在开发的时候,对于数值之间的转换一定要注意,无论是数字转字符串,还是 intfloatdouble 类型之间的相互转换,都要注意他们之间的精度。所以大家在做对精度有要求的数值转换的时候,用 double 类型来做。

消失的一分钱_数据_05


大家看个示例:

消失的一分钱_十进制数_06

当8、9位数的时候,精度就缺失了,八位数 80000031/100f 少了0.01,九位数 200000031/100f 直接少了0.31,但是五六七位数就没事,所以可见 float 的精度就只有七位,大于七位的精度直接缺失。另外再看下 double 类型的示例:

消失的一分钱_c#_07


到了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 类型牺牲了数值范围以换取更高的精度。

在用的时候显示指定用 mM ,像这样:

decimal decimalNumber = 1000.00m;

所以大家根据自己的开发需求选择合适的类型。

还有一点,就是大家对于数字类型定义的属性一定要清晰,就比如我一直拿 float 类型当 小数点后两位的数,即 xxx.00 来使用,所以多多少少会给开发埋雷

消失的一分钱_unity开发_08

这篇的内容就到此结束了,下期见,拜拜!

消失的一分钱_c#_09