恶心的0.5四舍五入问题

四舍五入是财务类应用中常见的需求,按中国人的财务习惯,遇到0.5统一向上进位,但是c#与java中默认的却不是这样。

见c#代码:

1         static void Main(string[] args)
2         {
3             Decimal d = 301353.05M;
4             Console.WriteLine(d);//301353.05
5             Console.WriteLine(Math.Round(d, 1));//301353.0
6             Console.WriteLine(Math.Round(d, 1, MidpointRounding.AwayFromZero));//301353.1
7
8             Console.ReadKey();
9         }

默认情况下,如果要舍弃的位置上,正好值是5,系统会看前一位是奇数还是偶数,如果是偶数,则丢弃最后1位,即上面代码行5,输出的结果为 301353.0,这不符合国人的习惯,所以要人为指定第3个参数"MidpointRounding.AwayFromZero"

java中也提出了类似的做法,但是有“缺陷”

1     @Test
2     public void testScale(){
3         double d = 301353.05;
4         BigDecimal decimal = new BigDecimal(d);
5         System.out.println(decimal);//301353.0499999999883584678173065185546875
6         System.out.println(decimal.setScale(1, RoundingMode.HALF_UP));//301353.0
7     }

类似的,在设置精度时,可以指定一个额外的参数RoundingMode.HALF_UP,表示如果要舍弃的这一位正好是5,则向上进位,代码看似没有问题,但是输出值却是301353.0

原因在于BigDecimal在计算机内部的存储值为"301353.0499999999883584678173065185546875",即小数点第2位是4,上面的代码要求精度到1位,所以代码执行时,只看第2个小数位,其值为4,没有到HALF的标准,因此直接扔掉

改进方法:

1     @Test
2     public void testScale(){
3         double d = 301353.05 + 0.0000000001;
4         BigDecimal decimal = new BigDecimal(d);
5         System.out.println(decimal);//301353.0500000001047737896442413330078125
6         System.out.println(decimal.setScale(1, RoundingMode.HALF_UP));//301353.1
7     }

在满足财务精度的前提下,将要处理的数字加1个微小的偏移量,这样计算机内部存储时,值变成301353.0500000001047737896442413330078125,这样小数位第2位变成了5,满足了HALF_UP的条件。

当然,这是权宜之计,如果大家有更好的通用方法,欢迎指正。

时间: 2024-10-13 08:56:50

恶心的0.5四舍五入问题的相关文章

Java中关于 BigDecimal 的一个导致double精度损失的"bug"

背景 在博客 恶心的0.5四舍五入问题 一文中看到一个关于 0.5 不能正确的四舍五入的问题.主要说的是 double 转换到 BigDecimal 后,进行四舍五入得不到正确的结果: public class BigDecimalTest { public static void main(String[] args){ double d = 301353.05; BigDecimal decimal = new BigDecimal(d); System.out.println(decima

BigDecimal四舍五入保留两位小数

import java.math.BigDecimal; import java.text.DecimalFormat; import java.text.NumberFormat; public class NumberFormatDemo { public static void main(String[] args) { // BigDecimal // 保留两位小数 System.out.println(new BigDecimal(0.2).setScale(2, BigDecimal

SQL Server2008函数大全(完整版)

SQL2008 表达式:是常量.变量.列或函数等与运算符的任意组合. 1. 字符串函数 函数 名称 参数 示例 说明 ascii(字符串表达式) select ascii('abc') 返回 97 返回字符串中最左侧的字符的ASCII 码. char(整数表达式) select char(100) 返回 d 把ASCII 码转换为字符. 介于0 和 255 之间的整数.如果该整数表达式不在此范围内,将返回 NULL 值. charindex(字符串表达式 1, 字符串表达式2[,整数表达式])

快速傅里叶算法模板

1 /* 2 algorithm : High-Precision FFT 3 4 */ 5 #include <cstdio> 6 #include <cstring> 7 #include <cmath> 8 #include <algorithm> 9 #define N 200005 10 #define pi acos(-1.0) // PI值 11 using namespace std; 12 struct complex 13 { 14 do

Math类的round方法小解

在Math类中有三个关于“四舍五入”的静态方法(ceil,floor,round): ① Math.ceil(number) 向上取整,Math.ceil(11.2) 结果:12              Math.ceil(11.6) 结果:12 Math.ceil(-11.2) 结果:-11            Math.ceil(-11.6) 结果:-11 ② Math.floor(number) 向下取整, Math.floor(11.2) 结果11              Math

Sqlserver 函数

SQL2008 表达式:是常量.变量.列或函数等与运算符的任意组合. 1. 字符串函数 函数 名称 参数 示例 说明 ascii(字符串表达式) select ascii('abc') 返回 97 返回字符串中最左侧的字符的ASCII 码. char(整数表达式) select char(100) 返回 d 把ASCII 码转换为字符. 介于0 和 255 之间的整数.如果该整数表达式不在此范围内,将返回 NULL 值. charindex(字符串表达式 1, 字符串表达式2[,整数表达式])

hihocoder #1083 : 积分

时间限制:1000ms 单点时限:1000ms 内存限制:256MB 描述 在平面上有一个顶点数为N的多边形P,区域 你需要写一个程序计算这个积分 输入 输入包含T (T<=500)个测试用例.数字T在输入文件的第一行给出.每个测试用例的第一行是一个整数N代表多边形的点数.其后跟随N行,每行包含两个点Xi和Yi,表示第i个点的坐标,当我们以给定的顺序连接这些点,我们得到了一个多边形.题目保证多边形不自交,这个多边形的面积也不会是零,也不会出现3个相邻的点共线的情况. [参数说明] 3 <= N

step by step 之餐饮管理系统三

1.说明 表名的长度最长为18个字符 茶色的字段为主键或联合主键 浅黄色的字段为索引 浅灰底色的字段为临时表中比正式表多出的字段 数据库系统:Sqlserver2008 脚本工具:使用CodeGenerate生成文档 数据库名称: rmsdb 事件探查器:AnjLab 2       表结构 说明,此表结构是在powerdesigner15工具上设计好后由下面的工具生成,sql脚本可以正确执行 2.1.1          用户表Users 表名(中文) 表名(英文) 字段前缀 用户表 User

poj2502Subway(SPFA)

题目链接: huangjing 分析: 首先说我做这题在哪里出现误区.. [1] 首先一条线路上的地铁站只有相邻的两站可以到达,比如一站和3站就不能直接到达,所以建边的时候应该用步行建边....还有就是数据给的同一条线路上的地铁站的纵坐标都相同,所以比较误导人... [2]最后的结果要四舍五入,就是精度的问题要注意... [3]还有一个小知识  double类型的不能初始化为-1... 这个题没a出来,哎  太忧伤了... 题目: Language: Default Subway Time Li