最近遇到一个问题,各个类型资金占总资金的百分比,计算后客户端以图饼的形式展示。
文章目录
- 方法一:利用java中的Bigdecimal 进行统计
- 方法二 差减法
- 方法三 递增加一法
方法一:利用java中的Bigdecimal 进行统计
代码如下:
@Test
public void test() {
double a = 5, b = 11, c = 13, d = 22, e = 36;
long sum = 87;
BigDecimal bigDecimal = new BigDecimal(a / sum).setScale(2, BigDecimal.ROUND_HALF_EVEN);
BigDecimal bigDecimal1 = new BigDecimal(b / sum).setScale(2, BigDecimal.ROUND_HALF_EVEN);
BigDecimal bigDecimal2 = new BigDecimal(c / sum).setScale(2, BigDecimal.ROUND_HALF_EVEN);
BigDecimal bigDecimal3 = new BigDecimal(d / sum).setScale(2, BigDecimal.ROUND_HALF_EVEN);
BigDecimal bigDecimal4 = new BigDecimal(e / sum).setScale(2, BigDecimal.ROUND_HALF_EVEN);
System.out.println(Double.parseDouble(bigDecimal.toString()));
System.out.println(bigDecimal1);
System.out.println(bigDecimal2);
System.out.println(bigDecimal3);
System.out.println(bigDecimal4);
BigDecimal add = bigDecimal.add(bigDecimal1).add(bigDecimal2).add(bigDecimal3).add(bigDecimal4);
System.out.println(add);
}
我们发现最后总和还确实为1,可以可以,但是这只是我们在随机情况下进行测试,表面好像没问题,其实有的时候还是会导致精度问题,比如下面这样。
@Test
public void test1() {
double a = 3, b = 3, c = 3;
long sum = 9;
BigDecimal bigDecimal = new BigDecimal(a / sum).setScale(2, BigDecimal.ROUND_HALF_EVEN);
BigDecimal bigDecimal1 = new BigDecimal(b / sum).setScale(2, BigDecimal.ROUND_HALF_EVEN);
BigDecimal bigDecimal2 = new BigDecimal(c / sum).setScale(2, BigDecimal.ROUND_HALF_EVEN);
System.out.println(Double.parseDouble(bigDecimal.toString()));
System.out.println(bigDecimal1);
System.out.println(bigDecimal2);
BigDecimal add = bigDecimal.add(bigDecimal1).add(bigDecimal2);
System.out.println(add);
}
这个原因是因为 BigDecimal 中 BigDecimal.ROUND_HALF_EVEN
这个静态类型造成的,具体参数意义如下:
ROUND_CEILING
Rounding mode to round towards positive infinity.
向正无穷方向舍入
ROUND_DOWN
Rounding mode to round towards zero.
向零方向舍入
ROUND_FLOOR
Rounding mode to round towards negative infinity.
向负无穷方向舍入
ROUND_HALF_DOWN
Rounding mode to round towards "nearest neighbor" unless both neighbors are equidistant, in which case round down.
向(距离)最近的一边舍入,除非两边(的距离)是相等,如果是这样,向下舍入, 例如1.55 保留一位小数结果为1.5
ROUND_HALF_EVEN
Rounding mode to round towards the "nearest neighbor" unless both neighbors are equidistant, in which case, round towards the even neighbor.
向(距离)最近的一边舍入,除非两边(的距离)是相等,如果是这样,如果保留位数是奇数,使用ROUND_HALF_UP ,如果是偶数,使用ROUND_HALF_DOWN
ROUND_HALF_UP
Rounding mode to round towards "nearest neighbor" unless both neighbors are equidistant, in which case round up.
向(距离)最近的一边舍入,除非两边(的距离)是相等,如果是这样,向上舍入, 1.55保留一位小数结果为1.6
ROUND_UNNECESSARY
Rounding mode to assert that the requested operation has an exact result, hence no rounding is necessary.
计算结果是精确的,不需要舍入模式
ROUND_UP
Rounding mode to round away from zero.
向远离0的方向舍入
其实这样的话并不是我们的效果,因为客户端想要展示的是百分百,如果是对于要求不严的,可以用这种方法去写。
方法二 差减法
这个方法就是我先计算前 n - 1
个,然后在用 1 去减前面的和。
但是这里有个问题,就是我们是一个 double
的,那么可能减后的结果就变成了 3.999999999 这样的形式,所以还需要处理,思路比较简单,也就不黏贴了。
方法三 递增加一法
这个是我参考的谷歌的一个帖子的思路:http://www.dovov.com/100-9.html,
但不知为什么排版很混乱,不过思路还是很不错的。
现在我们将资金的各个百分比计算出
13.626332% 47.989636% 9.596008% 28.788024%
我们拿到整数部分和小数部分
整数部分
13 47 9 28 总和为97
小数部分
.626332% .989636% .596008% .788024%
我们依次比对小数部分哪个最大,然后将对应的整数部分加一。
现在是.989636% 最大,那么将47+1=48,
现在总和为 13 + 48 + 9 + 28 =98,还不够 100,
我们在去小数部分中找第二大的,.788024% ,将此对应的整数部分加一,
28 + 1 = 29,
现在的总和为 13 + 48 + 9 + 29 =99,
发现还是不够一百,那么在找小数部分第三大的,.626332% ,对应的整数部分为13 + 1 = 14,
现在的总和为 14 + 48 + 9 + 29 =100,
那么我们返回的结果就为 14, 48, 9, 29
代码:
@Test
public void test2() {
List<Long> list = new ArrayList<>();
list.add(3522L);
list.add(15000L);
list.add(220L);
list.add(14562L);
list.add(555L);
list.add(666L);
list.add(120L);
List<Integer> list1 = listStatistics(list);
System.out.println(list1);
}
public List<Integer> listStatistics(List<Long> curList) {
if (curList.size() == 0) {
return null;
}
double sum = 0;
for (Long aLong : curList) {
sum += aLong;
}
List<Integer> integerList = new ArrayList<>();
List<Double> doubleList = new ArrayList<>();
for (Long aLong : curList) {
double v = aLong / sum * 100;
int vInt = (int) v;
integerList.add(vInt);
doubleList.add(v - vInt);
}
int curSum = 0;
for (int i = 0; i < curList.size(); i++) {
curSum += integerList.get(i);
}
while (curSum < 100) {
int index = 0;
for (int i = 1; i < doubleList.size(); i++) {
if (doubleList.get(i) > doubleList.get(index)) {
index = i;
}
}
Integer integer = integerList.get(index);
integerList.set(index, ++integer);
doubleList.set(index, 0.0);
curSum++;
}
return integerList;
}
有时候我们需要的数据为27.8这样的,其实思路还是一样的,就是我们扩大1000 倍,然后最后在除以 10就好。