关于商业数据的运算问题
Java中的简单浮点数类型float和double不能够进行运算,或者运算会丢失精度,不光是Java,在其它很多编程语言中也有这样的问题。在大多数情况下,计算的结果是准确的,float和double只能用来做科学计算或者是工程计算,在商业计算中我们要用 java.math.BigDecimal,要注意该构造函数是一个精确的转换,它无法得到与先调用Double.toString(double)方法将double转换成String,再使用BigDecimal(String)构造函数一样的结果。如果要达到这种结果,应该使用new BigDecimal(Stringvalue) 或 BigDecimal.valueof( double value)。
金额转千分位及中文显示
保证精度的思路
先用BigDecimal对数据进行保留位数处理,将返回的数据通过DecimalFormat做千分位处理。
DecimalFormat 在使用小数保留位数时,须用:
setRoundingMode方法:默认保留2位 + 保留规则。
setScale方法:可指定保留位数 + 保留规则。
商业计算高精确结果,指定去尾规则保留2位小数位数
对结果精度要求高,使用:BigDecimal
四舍五入并保留两位有效数字
BigDecimal a = BigDecimal.valueOf(2.235);
//RoundingMode可指定多种去尾规则
BigDecimal b =a.setScale(2, RoundingMode.HALF_UP);//四舍五入后并保留两位小数
System.out.println("结果:"+b);
千分位财务显示
DecimalFormat中#和0的区别:
#用于整数部分没有数位的限制,用于小数部分表示最多只能有多少位小数的意思,多余的进行舍入。
eg:
data:02345.50
pattern:",###.##"
result:2,345.5
0:限制整数部分最少出现的位数;不足这个位数的,补0
eg:
data:34.5
pattern:"0,000.00"
result:0,034.50
根据需求将#和0搭配使用
//保留两位小数并展示千分位符
DecimalFormat df1 = new DecimalFormat("##,##0.00");
System.out.println(df1.format(0.22));// 0.22
DecimalFormat不准确问题 做保留两位小数时特殊情况
DecimalFormat df = new DecimalFormat("#0.00");
有个差异点需要注意:
System.out.println(df.format(0.235));// 显示0.23
System.out.println(df.format(0.2351));// 显示0.24, 因为0.2351距离0.24更近。
示例代码:
double d_qualityGuaranteeDeposit = MoneyConvertUtils.number2Precision(Double.valueOf(qualityGuaranteeDeposit));
newContract.setQualityGuaranteeDeposit(d_qualityGuaranteeDeposit);//转保留两位有效数字
newContract.setQualityGuaranteeDepositForShow(MoneyConvertUtils.number2ShowStr(BigDecimal.valueOf(d_qualityGuaranteeDeposit)));//double转千分位
newContract.setIsQualification(MoneyConvertUtils.number2CNMontrayUnit(BigDecimal.valueOf(d_qualityGuaranteeDeposit)));//double转中文
/**
* 金额转换
*
* @author Admin
*
*/
public class MoneyConvertUtils {
/**
* 汉语中数字大写
*/
private static final String[] CN_UPPER_NUMBER = { "零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖" };
/**
* 汉语中货币单位大写,这样的设计类似于占位符
*/
private static final String[] CN_UPPER_MONETRAY_UNIT = { "分", "角", "元", "拾", "佰", "仟", "万", "拾", "佰", "仟", "亿", "拾",
"佰", "仟", "兆", "拾", "佰", "仟" };
/**
* 特殊字符:整
*/
private static final String CN_FULL = "整";
/**
* 特殊字符:负
*/
private static final String CN_NEGATIVE = "负";
/**
* 金额的精度,默认值为2
*/
private static final int MONEY_PRECISION = 2;
/**
* 特殊字符:零元整
*/
private static final String CN_ZEOR_FULL = "零元" + CN_FULL;
/**
* 金额保留两位小数,并转千分位
*
* @param dl:double类型金额
* @return
*/
public static Double number2Precision(Double dl) {
BigDecimal a = BigDecimal.valueOf(dl);
BigDecimal b = a.setScale(MONEY_PRECISION, RoundingMode.HALF_UP);
DecimalFormat df1 = new DecimalFormat("##,##0.00");
return Double.parseDouble(df1.format(b.toString()));
}
/**
* 金额保留两位小数,并转千分位
*
* @param dl:double类型金额
* @return
*/
public static String number2ShowStr(BigDecimal numberOfMoney) {
BigDecimal b = numberOfMoney.setScale(MONEY_PRECISION, RoundingMode.HALF_UP);// 四舍五入并保留两位小数
DecimalFormat df1 = new DecimalFormat("##,##0.00");
System.out.println(df1.format(b.doubleValue()));// 0.22
return b.toString();
}
/**
* 金额转RMB大写
*
* @param dl:double类型金额
* @return
*/
public static String number2CNMontrayUnit(BigDecimal numberOfMoney) {
StringBuffer sb = new StringBuffer();
// -1, 0, or 1 as the value of this BigDecimal is negative, zero, or
// positive.
int signum = numberOfMoney.signum();
// 零元整的情况
if (signum == 0) {
return CN_ZEOR_FULL;
}
// 这里会进行金额的四舍五入
long number = numberOfMoney.movePointRight(MONEY_PRECISION).setScale(0, 4).abs().longValue();
// 得到小数点后两位值
long scale = number % 100;
int numUnit = 0;
int numIndex = 0;
boolean getZero = false;
// 判断最后两位数,一共有四中情况:00 = 0, 01 = 1, 10, 11
if (!(scale > 0)) {
numIndex = 2;
number = number / 100;
getZero = true;
}
if ((scale > 0) && (!(scale % 10 > 0))) {
numIndex = 1;
number = number / 10;
getZero = true;
}
int zeroSize = 0;
while (true) {
if (number <= 0) {
break;
}
// 每次获取到最后一个数
numUnit = (int) (number % 10);
if (numUnit > 0) {
if ((numIndex == 9) && (zeroSize >= 3)) {
sb.insert(0, CN_UPPER_MONETRAY_UNIT[6]);
}
if ((numIndex == 13) && (zeroSize >= 3)) {
sb.insert(0, CN_UPPER_MONETRAY_UNIT[10]);
}
sb.insert(0, CN_UPPER_MONETRAY_UNIT[numIndex]);
sb.insert(0, CN_UPPER_NUMBER[numUnit]);
getZero = false;
zeroSize = 0;
} else {
++zeroSize;
if (!(getZero)) {
sb.insert(0, CN_UPPER_NUMBER[numUnit]);
}
if (numIndex == 2) {
if (number > 0) {
sb.insert(0, CN_UPPER_MONETRAY_UNIT[numIndex]);
}
} else if (((numIndex - 2) % 4 == 0) && (number % 1000 > 0)) {
sb.insert(0, CN_UPPER_MONETRAY_UNIT[numIndex]);
}
getZero = true;
}
// 让number每次都去掉最后一个数
number = number / 10;
++numIndex;
}
// 如果signum == -1,则说明输入的数字为负数,就在最前面追加特殊字符:负
if (signum == -1) {
sb.insert(0, CN_NEGATIVE);
}
// 输入的数字小数点后两位为"00"的情况,则要在最后追加特殊字符:整
if (!(scale > 0)) {
sb.append(CN_FULL);
}
return sb.toString();
}
}
解决100.80 显示一百元零捌角的问题
//整数部分的人民币大写
private static final String[] NUMBERS = {"零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"};
//数位部分
private static final String[] IUNIT = {"元", "拾", "佰", "仟", "万", "拾", "佰", "仟", "亿", "拾", "佰", "仟", "兆", "拾", "佰", "仟"};
//小数部分的人民币大写
private static final String[] DUNIT = {"角", "分", "厘"};
private static final String CN_FULL = "整";
private static final String CN_NEGATIVE = "负";//特殊字符:负
private static final int MONEY_PRECISION = 2;
private static final String CN_ZEOR_FULL = "零元" + CN_FULL;
//转成中文的大写金额
public static String toChinese(String str) {
//判断输入的金额字符串
if ("0".equals(str) || "0.00".equals(str) || "0.0".equals(str)) {
return CN_ZEOR_FULL;
}
//判断是否存在负号"-"
boolean flag = false;
if (str.startsWith("-")) {
flag = true;
str = str.replaceAll("-", "");
}
//如果输入字符串中包含逗号,替换为 "."
str = str.replaceAll(",", ".");
String integerStr;//整数部分数字
String decimalStr;//小数部分数字
//分离整数部分和小数部分
if (str.indexOf(".") > 0) {//整数部分和小数部分
integerStr = str.substring(0, str.indexOf("."));
decimalStr = str.substring(str.indexOf(".") + 1);
} else if (str.indexOf(".") == 0) {//只存在小数部分 .34
integerStr = "";
decimalStr = str.substring(1);
} else { //只存在整数部分 34
integerStr = str;
decimalStr = "";
}
//整数部分超出计算能力,直接返回
if (integerStr.length() > IUNIT.length) {
System.out.println(str + ":超出计算能力");
return str;
}
//整数部分存入数组 目的是为了可以动态的在字符串数组中取对应的值
int[] integers = toIntArray(integerStr);
//判断整数部分是否存在输入012的情况
if (integers.length > 1 && integers[0] == 0) {
System.out.println("抱歉,请输入数字!");
if (flag) {
str = "-" + str;
}
return str;
}
boolean isWan = isWanUnits(integerStr);//设置万单位
//小数部分数字存入数组
int[] decimals = toIntArray(decimalStr);
String result = getChineseInteger(integers, isWan) + getChineseDecimal(decimals);//返回最终的大写金额
if (flag) {
return CN_NEGATIVE + result;//如果是负数,加上"负"
} else {
return result;
}
}
//将字符串转为int数组
private static int[] toIntArray(String number) {
//初始化一维数组长度
int[] array = new int[number.length()];
//循环遍历赋值
for (int i = 0; i < number.length(); i++) {
array[i] = Integer.parseInt(number.substring(i, i + 1));
}
return array;
}
//将整数部分转为大写的金额
public static String getChineseInteger(int[] integers, boolean isWan) {
StringBuffer chineseInteger = new StringBuffer("");
int length = integers.length;
// 对于输入的字符串为 "0." 存入数组后为 0
if (length == 1 && integers[0] == 0) {
return "";
}
for (int i = 0; i < length; i++) {
String key = "";//0325464646464
if (integers[i] == 0) {
if ((length - i) == 13)//万(亿)
key = IUNIT[4];
else if ((length - i) == 9) {//亿
key = IUNIT[8];
} else if ((length - i) == 5 && isWan) {//万
key = IUNIT[4];
} else if ((length - i) == 1) {//元
key = IUNIT[0];
}
if ((length - i) > 1 && integers[i + 1] != 0) {
key += NUMBERS[0];
}
}
chineseInteger.append(integers[i] == 0 ? key : (NUMBERS[integers[i]] + IUNIT[length - i - 1]));
}
return chineseInteger.toString();
}
//将小数部分转为大写的金额
private static String getChineseDecimal(int[] decimals) { //角 分 厘 038 壹分捌厘
StringBuffer chineseDecimal = new StringBuffer("");
String flag0 = "";
for (int i = 0; i < decimals.length; i++) {
if (i == 3) {
break;
}
chineseDecimal.append(decimals[i] == 0 ? "" : (NUMBERS[decimals[i]] + DUNIT[i]));
}
System.out.println("chineseDecimal:" + chineseDecimal);
if("".equals(chineseDecimal.toString())){
//如果小数部分为00或者不存在时部位 整
chineseDecimal.append(CN_FULL);
}else if (chineseDecimal.indexOf("角") != -1 && chineseDecimal.indexOf("分") == -1){
// 有角没分
chineseDecimal.append(CN_FULL);
}
return flag0 + chineseDecimal.toString();
}
//判断当前整数部分是否已经是达到【万】
private static boolean isWanUnits(String integerStr) {
int length = integerStr.length();
if (length > 4) {
String subInteger = "";
if (length > 8) {
subInteger = integerStr.substring(length - 8, length - 4);
} else {
subInteger = integerStr.substring(0, length - 4);
}
return Integer.parseInt(subInteger) > 0;
} else {
return false;
}
}
调用:
double dl = -508.2;
BigDecimal a = BigDecimal.valueOf(dl);
BigDecimal b = a.setScale(MONEY_PRECISION, RoundingMode.HALF_UP);
String afterStr = toChinese(b.toString());
System.out.println(b.toString() + ": " + afterStr);