这两天排查一个bug,数据库中同样的计算公式 和 c#代码中一毛一样的计算公式,最后结果为 decimal 类型的小数,需求保留两位小数。但是结果是数据库中 和 代码中的结果总是对不上。
整理并简化了一下项目中代码计算公式:
decimal a = 3, b = 0, c = 0.035;
var s = math.round((a - b) * c, 2);
问题来了,项目数据库用的sqlserver,上面同样公式,数据库算出来是 0.11,而c# 代码中算出来是 0.10。
如果不保留小数,原始结果都知道是 0.105,那么为什么四舍五入后,结果为啥却不一样呢??
一开始,我也没有想明白,盯着代码愣了半天,就好像自己的考试一样,明明觉得得了100分,为啥最后还是99。
后来上了个厕所回来,抛开所有疑问,看看是不是这个 取整函数的问题。于是,发现 math.round 有很多个重载函数,于是 f12 进去看了看,发现有个参数 midpointrounding 的枚举值,再 f12 进去看,发现有两个东西 toeven、awayfromzero。嗯?!。。应该是这里的问题,继续探索!
查了一些资料,微软官方对这两个枚举值的解释如下:

公司项目比较老,上述截图是 c# 7.0 里面的,其中还多了几个枚举值,这里不说明,有兴趣自己百度吧!我就说跟我遇到问题最相关的这个枚举值 awayfromzero。
提到 awayfromzero 这个,还需要首先了解一下“四舍五入”的理解:
(1)中国人的理解:1~4就舍去,5~9就进1
(2)外国人的理解:按照ieee的标准,默认是 1~5就舍去,6~9就进1
好吧,问题就出现在我们理解的“四舍五入”的最中间的 5。awayfromzero 的参数就是告知代码逻辑,遇到 5 的时候,进1处理。
回到问题本身,说明项目中的数据库是将 5 进1 了,那么代码中也改为 5进1的规则即可。于是将上述代码修改为这样,就解决了问题:
var s = math.round((a - b) * c, 2, midpointrounding.awayfromzero); // 结果为 0.11
反思,即便是一个自认为很小的问题,有可能会让你的见识焕然一新!
发表评论