一、问题描述:
最近用java Bigdecimal 四舍五入方式做了几个百分比统计图表,可是最后的几个汇总的和总是100.1,或者99.97。百度,google了好久。下面三种方案,第2)3)中还是会有处理不掉的情况,比如 1,1,1和是3。每个1/3 约等于33.33,没办法,我的选择是这种不做处理。三个都是1,凭啥其中一个就是33.34!
二、解决方案:
1)如果统计中有其他或者不重要的统计项的,这个不计算,等别的计算完了,再用100减去其他的和。
(听着挺合理 的,大家可以试试,我这个没有其他,所以这个不行)
2)google找到的一个帖子http://dovov.com/100-9.html 通过将小数部分递减的顺序加1来分配差异
3)这个我自创的,每次向前进一位,还是四舍五入,一直进到个位,如何还是不等于100,就不做处理。基本90%的情况都可以和变成100%。
三、相关代码
第 2)种的实现代码
public static void main(String[] args) {
getResultWithGoogleSuggestion();
}
private static void getResultWithGoogleSuggestion() {
List<Double> result = new ArrayList<Double>();
// 13.626332% 47.989636% 9.596008% 28.788024
result.add(13.62);
result.add(47.98);
result.add(9.59);
result.add(28.78);
result.add(0d);
Collections.sort(result, new Comparator<Double>() {//按小数点的指定位数排序
@Override
public int compare(Double o1, Double o2) {
return (getValue(String.valueOf(o2), 2))-(getValue(String.valueOf(o1), 2));
}
});
double sum = 0;
for (int i = 0; i < result.size(); i++) {
sum = BigDecimalUtils.add(sum, result.get(i));
}
double d=BigDecimalUtils.sub(sum,100);
double b=turnLastToOne(d);
int w=getTimes(d);
boolean doOrNot=true;
if(d>0){//和100比较大
Collections.reverse(result);
if(getEquelsNum(result)>w){
doOrNot=false;
}
if(doOrNot) {
for (int i = 0; i < w; i++) {
result.set(i, BigDecimalUtils.sub(result.get(i), b));
}
}
}else if(d<0){//和比100小,要在指定几个上加上
if(getEquelsNum(result)>w){
doOrNot=false;
}
if(doOrNot) {
for (int i = 0; i < w; i++) {
result.set(i, BigDecimalUtils.sub(result.get(i), b));
}
}
}
System.out.println(result);
}
private static int getEquelsNum(List<Double> result) {
int k=1;
double dd=-1d;
for(double d:result){
if(dd!=-1){
if(dd==d){
k++;
}
}else{
dd=d;
}
}
return k;
}
private static int getTimes(double d) {
String s=String.valueOf(d);
return Integer.parseInt(s.substring(
s.length()-1,s.length()));
}
private static double turnLastToOne(double d) {//获得指定位置上的数字
String s=String.valueOf(d);
s=s.substring(0,s.length()-1);
return Double.parseDouble(s+"1");
}
private static int getValue(String s, int position) {//获得指定位置上的数字
try {
return Integer.parseInt(s.substring(s.indexOf(".") + position, s.indexOf(".") + position + 1));
}catch (StringIndexOutOfBoundsException e){
return -1;
}
}
第 3)种的实现代码
主方法:
/* 处理百分数之和大于100的问题 start*/
decimalPointGoUpOne(result,CommonConstants.ONE);//进一位看看
decimalPointGoUpOne(result,CommonConstants.ZERO);//如果不对,再进一位
/* 处理百分数之和大于100的问题 end*/
double sumResult=CommonConstants.DOUBLE_ZERO;
for(int i=0;i<result.size();i++){
sumResult= BigDecimalUtils.add(sumResult,result.get(i));
}
if (sumResult!=CommonConstants.ONE_HUNDRED){//进了2次都不能搞定,说明无限循环情况
result= result2;//返回原来的值
}
子方法:
private void decimalPointGoUpOne(List<Double> result,int scale) {
double sumResult=CommonConstants.DOUBLE_ZERO;
for(int i=0;i<result.size();i++){
sumResult= BigDecimalUtils.add(sumResult,result.get(i));
}
if (sumResult!=CommonConstants.ONE_HUNDRED){ //通过将小数部分递减的顺序加1来分配差异 好厉害啊
for(int i=0;i<result.size();i++) {
result.set(i,BigDecimalUtils.round(result.get(i),scale));
}
}
}
第二种测试代码:
链接:https://pan.baidu.com/s/1InT2TWSvyFRA3dYOOLZROw
提取码:2oro
第三种测试代码:
链接:https://pan.baidu.com/s/1IGkci2k7w4ELKVF-A_dazA
提取码:pdx2
鄙人才疏学浅,代码仅供参考。有什么问题,还请各位大神不吝赐教。