• 金额精确计算,必须使用BigDecimal;
  • 平均分摊,分摊的零头,一般都是由数据“精度”和分摊系数决定的;
  • 主要是如何对零头进行处理,保证尽可能的平均分配。

1、按户均摊

/**
	 * 按户数分摊方式
	 * 分摊计算采用截取小数精确位数后的小数值BigDecimal.ROUND_DOWN
	 * 目的:指定金额200元,平均分配给6户,分摊前后金额相等
	 */
	private static void hsFt() {
		BigDecimal[] repair_shou_Amt = new BigDecimal[6];// 每个分户应承担维修金额
		BigDecimal fact_repair_Amt = new BigDecimal("0");// 维修对象的实际费用(不含零头)

		//待分摊金额
		BigDecimal ft_amt = new BigDecimal("200");
		//分户总数
		int count = repair_shou_Amt.length;

		for(int i = 0; i < count; i++) {
			//求每个分户的承担金额=分摊金额/总户数
			repair_shou_Amt[i] =ft_amt.divide(new BigDecimal(count), 2, BigDecimal.ROUND_DOWN); //截掉精度之后的小数位的值

			//实际分摊承担总金额---小数点截取后的总金额
			fact_repair_Amt = fact_repair_Amt.add(repair_shou_Amt[i]);
		}
		// 零头 = 分摊实际承担金额-待分摊金额
		BigDecimal repair_oddment_amt = fact_repair_Amt.subtract(ft_amt);

		// 算出要参与分摊零头的户数-截掉精度后的小数位的值累加后的每个值假设最大也只可能为0.00999999999999999999无限接近0.01
		int repair_int = repair_oddment_amt.multiply(new BigDecimal("100")).abs().intValue();
		// 把维修金额零头分摊到分户上,如果参与维修零头的户数大于零或者小于总户数,则进行分摊,否则报错
		if( (repair_int < count) && (repair_int > 0) ) {
			// 如果零头小于零,则说明有repair_int个分户承担的金额少一分钱0.01,需要加一分钱
			if( repair_oddment_amt.compareTo(new BigDecimal("0")) < 0 ) {
				for(int i = 0; i < repair_int; i++) {
					repair_shou_Amt[i] = repair_shou_Amt[i].add(new BigDecimal("0.01"));
				}
			}
			else {
				// 如果零头大于零,则说明有repair_int个分户承担的金额多一分钱0.01,需要减去一分钱
				for(int i = 0; i < repair_int; i++) {
					repair_shou_Amt[i] = repair_shou_Amt[i].subtract(new BigDecimal("0.01"));
				}
			}
		}
		else if( repair_int == 0 ) {
			// 零头如果为零,不需要处理
		}
		else {
			throw new BusinessException("非法操作!");
		}
	}

2、按面积均摊

/**
	 * 按面积分摊方式
	 * 分摊计算采用截取小数精确位数后的小数的值BigDecimal.ROUND_DOWN
	 * 目的:指定金额200元,平均分配给6户,每户面积可能不等,分摊前后金额相等
	 */
	public static void areaFt(){
		BigDecimal[] repair_shou_Amt = new BigDecimal[6];// 每个分户应承担维修金额
		BigDecimal fact_repair_Amt = new BigDecimal("0");// 维修对象的实际费用(不含零头)

		//待分摊金额
		BigDecimal ft_amt = new BigDecimal("200");
		//分户总数
		int count = repair_shou_Amt.length;
		//分户面积
		BigDecimal[]info_area = {new BigDecimal("20.12"),new BigDecimal("10.12"),new BigDecimal("11.12"),new BigDecimal("15.12"),new BigDecimal("10.12"),new BigDecimal("110.12")};

		//分摊的总建面积
		BigDecimal totalArea = ObjectUtil.getZeroBigDecimal();
		for(int i = 0 ; i < count; i++){
			totalArea=totalArea.add(info_area[i]);
		}

		for(int i = 0; i < count; i++) {
			BigDecimal oa_hou_area = info_area[i];
			//求每个分户的承担金额=(分户面积/总建筑面积)*分摊金额
			repair_shou_Amt[i] = (oa_hou_area.multiply(ft_amt).divide(totalArea, 2, BigDecimal.ROUND_DOWN));//截掉精度之后的小数位的值

			//实际分摊承担总金额---小数点截取后的总金额
			fact_repair_Amt = fact_repair_Amt.add(repair_shou_Amt[i]);
		}
		// 零头 = 分摊实际承担金额-待分摊金额
		BigDecimal repair_oddment_amt = fact_repair_Amt.subtract(ft_amt);

		// 算出要参与分摊零头的户数-截掉精度后的小数位的值累加后的每个值假设最大也只可能为0.00999999999999999999无限接近0.01
		int repair_int = repair_oddment_amt.multiply(new BigDecimal("100")).abs().intValue();
		System.out.println(repair_int);
		// 把维修金额零头分摊到分户上,如果参与维修零头的户数大于零或者小于总户数,则进行分摊,否则报错
		if( (repair_int < count) && (repair_int > 0) ) {
			// 如果零头小于零,则说明有repair_int个分户承担的金额少一分钱0.01,需要加一分钱
			if( repair_oddment_amt.compareTo(new BigDecimal("0")) < 0 ) {
				for(int i = 0; i < repair_int; i++) {
					repair_shou_Amt[i] = repair_shou_Amt[i].add(new BigDecimal("0.01"));
				}
			}
			else {
				// 如果零头大于零,则说明有repair_int个分户承担的金额多一分钱0.01,需要减去一分钱
				for(int i = 0; i < repair_int; i++) {
					repair_shou_Amt[i] = repair_shou_Amt[i].subtract(new BigDecimal("0.01"));
				}
			}
		}
		else if( repair_int == 0 ) {
			// 零头如果为零,不需要处理
		}
		else {
			throw new BusinessException("非法操作!");
		}
	}

2、按随机均摊

/**
	 * 按面积分摊方式
	 * 分摊计算采用四舍五入BigDecimal.ROUND_HALF_UP
	 * 目的:指定金额200元,平均分配给6户,每户面积可能不等,分摊前后金额相等
	 */
	public static void otherFt(){
		BigDecimal[] repair_shou_Amt = new BigDecimal[6];// 每个分户应承担维修金额
		BigDecimal fact_repair_Amt = new BigDecimal("0");// 维修对象的实际费用(不含零头)

		//待分摊金额
		BigDecimal ft_amt = new BigDecimal("200");
		//分户总数
		int count = repair_shou_Amt.length;
		//分户面积
		BigDecimal[]info_area = {new BigDecimal("20.12"),new BigDecimal("10.12"),new BigDecimal("11.12"),new BigDecimal("15.12"),new BigDecimal("10.12"),new BigDecimal("110.12")};

		//分摊的总建面积
		BigDecimal totalArea = ObjectUtil.getZeroBigDecimal();
		for(int i = 0 ; i < count; i++){
			totalArea=totalArea.add(info_area[i]);
		}

		for(int i = 0; i < count; i++) {
			BigDecimal oa_hou_area = info_area[i];
			//求每个分户的承担金额=(分户面积/总建筑面积)*分摊金额
			repair_shou_Amt[i] = (oa_hou_area.multiply(ft_amt).divide(totalArea, 2, BigDecimal.ROUND_HALF_UP));//四舍五入

			//实际分摊承担总金额---小数点四舍五入后的总金额
			fact_repair_Amt = fact_repair_Amt.add(repair_shou_Amt[i]);
		}
		// 零头 = 分摊实际承担金额-待分摊金额
		BigDecimal repair_oddment_amt = fact_repair_Amt.subtract(ft_amt);

		// 算出要参与分摊零头的户数-repair_int这个值的范围是由精度决定的,如果保留两位小数,则有且只可能存在1户
		int repair_int = repair_oddment_amt.multiply(new BigDecimal("100")).abs().intValue();
		System.out.println(repair_int);

		// 把维修金额零头分摊到分户上,如果参与维修零头的户数大于零或者小于总户数,则进行分摊,否则报错
		if( (repair_int < count) && (repair_int > 0) ) {
			//取分摊分户中的某一户随机补或减0.01
			Random rand = new Random();
			int randNum = rand.nextInt(6);
			System.out.println(randNum);

			// 如果零头小于零,则说明有repair_int个分户承担的金额少一分钱0.01,需要加一分钱
			if( repair_oddment_amt.compareTo(new BigDecimal("0")) < 0 ) {
				repair_shou_Amt[randNum] = repair_shou_Amt[randNum].add(new BigDecimal("0.01"));
			}
			else {
				// 如果零头大于零,则说明有repair_int个分户承担的金额多一分钱0.01,需要减去一分钱
				repair_shou_Amt[randNum] = repair_shou_Amt[randNum].subtract(new BigDecimal("0.01"));
			}
		}
		else if( repair_int == 0 ) {
			// 零头如果为零,不需要处理
		}
		else {
			throw new BusinessException("非法操作!");
		}
	}

JAVA金额按比例分摊,零头处理的更多相关文章

  1. java金额的加减乘除

    package com.wedge.edp.framework.common.util; import java.math.BigDecimal; /** * 金额的加减乘除 */ public cl ...

  2. java 金额的大小写转换类

    /** *金额大小写转换工具类 */ public class MoneyUtil { /** 大写数字 */ private static final String[] NUMBERS = { &q ...

  3. java 金额计算,商业计算 double不精确问题 BigDecimal,Double保留两位小数方法

    解决办法================== http://blog.javaxxz.com/?p=763 一提到Java里面的商业计算,我们都知道不能用float和double,因为他们无法 进行精 ...

  4. java 金额数字转换大写算法

    根据人民币大写金额规范,转换有几点要注意的: 阿拉伯数字中间有"0"时,中文大写金额中间可以只写一个"零"字.如¥1,409.50,应写成人民币壹仟肆佰零玖圆伍 ...

  5. JAVA金额格式字符串转数值

    项目中有时会遇到对金额格式的数值如“1,234.34567”进行计算,直接使用Double.parseDouble(“1,234.34567”)会抛出NumberFormatException异常, ...

  6. 前阿里CEO卫哲谈阿里创业经验:如何找人、找钱、找方向?(不同的阶段分别有:时间优先、金额优先、比例优先,不要做平台,太难)

    新浪科技李根 整理报道 卫哲现在是御嘉基金的创始合伙人,他另一个更加知名的身份是阿里巴巴(B2B)前CEO,在2006年到2011年的时间里,卫哲见证了阿里巴巴如何利用人才.资本和方向选择一路壮大. ...

  7. Java生鲜电商平台-订单架构实战

    Java生鲜电商平台-订单架构实战 生鲜电商中订单中心是一个电商后台系统的枢纽,在这订单这一环节上需要读取多个模块的数据和信息进行加工处理,并流向下一环节:因此订单模块对一电商系统来说,重要性不言而喻 ...

  8. Java生鲜电商平台-会员积分系统的设计与架构

    Java生鲜电商平台-会员积分系统的设计与架构 说明:互联网平台积分体系主要用于激励和回馈用户在平台的消费行为和活动行为,一个良好的积分体系可以很好的提升用户的粘性及活跃度. 一.互联网平台积分体系设 ...

  9. 用“逐步排除”的方法定位Java服务线上“系统性”故障(转)

    一.摘要 由于硬件问题.系统资源紧缺或者程序本身的BUG,Java服务在线上不可避免地会出现一些“系统性”故障,比如:服务性能明显下降.部分(或所 有)接口超时或卡死等.其中部分故障隐藏颇深,对运维和 ...

随机推荐

  1. python 获取utc时间转化为本地时间

    import datetime timenow = (datetime.datetime.utcnow() + datetime.timedelta(hours=8)) timetext = time ...

  2. 一步一步实现基于GPU的pathtracer(一):基础

    出于3D计算机图形学和图形渲染方面的个人兴趣,脑子里便萌生出了自己实现一个渲染器的想法,主要是借助pathtracing这种简单的算法,外加GPU加速来实现,同时也希望感兴趣的朋友们能够喜欢,也欢迎提 ...

  3. ASP.NET MVC5(一):ASP.NET MVC概览

    ASP.NET MVC概览 ASP.NET MVC是一种构建Web应用程序的框架,它将一般的MVC(Model-View-Controller)模式应用于ASP.NET框架. 1.ASP.NET MV ...

  4. VMware Workstation 11安装

    VMware Workstation 11序列号:1F04Z-6D111-7Z029-AV0Q4-3AEH8

  5. TypeScript 零基础入门

    前言 2015 年末看过一篇文章<ES2015 & babel 实战:开发 npm 模块>,那时刚接触 ES6 不久,发觉新的 ES6 语法大大简化了 JavaScript 程序的 ...

  6. Android搞事篇——使用Intent跳转界面

    跳转页面基本分为三个步骤: 1.初始化一个intent:(一个intent就够用了): 2.传入intent参数: 3.调用startactivity();实现跳转页面 具体操作如下 首先你需要一个项 ...

  7. javaCV开发详解之5:录制音频(录制麦克风)到本地文件/流媒体服务器(基于javax.sound、javaCV-FFMPEG)

    javaCV系列文章: javacv开发详解之1:调用本机摄像头视频 javaCV开发详解之2:推流器实现,推本地摄像头视频到流媒体服务器以及摄像头录制视频功能实现(基于javaCV-FFMPEG.j ...

  8. nginx四层负载均衡配置

    nginx四层负载均衡配置代理Mysql集群 环境如下: ip 192.168.6.203 Nginx ip 192.168.6.*(多台) Mysql 步骤一 查看Nginx是否安装stream模块 ...

  9. HTTPS协议入门

    目录什么是https?https的利与弊?https的原理和流程?什么是证书/CA证书?什么是单向SSL认证与双向SSL认证?网站如何实现https?网站实现https的一些补充说明参考网页 一.什么 ...

  10. socket套接字编程

    一.概述 1.socket是一种进程间通信方式,既可以用于一台机器,也可以用于网络.常用语C/S模型. 2.可以跨越Windows和Linux操作系统,可以跨越不同语言. 3.注意网络字节序和主机字节 ...