背景
最近工作里需要处理数字的展示,数字会被计算成 double 型数据,再转换到VO里,作为 XX.Y 展示出去。
问题
之前的处理逻辑如下面代码,当遇到 10.0 这样的数字时,展示的是 1E1 这样的科学计数法,这与期望的 10 不一样。
BigDecimal.valueOf(number).stripTrailingZeros())
原因
在 Java 中,BigDecimal 的输出格式是受其内部表示方式和 toString 方法的影响。
这里的关键点是:
- 浮点数的表示:当你使用
10.0(一个 double 值)来构造BigDecimal时,BigDecimal.valueOf(double)将其转化为字符串,而在这个过程中,浮点数可能会被以科学计数法表示。如果这个值被解析为10.0,那么它将被表示为1E+1。 - 内部数据结构:
BigDecimal用整型和幂的组合来表示数字,因此对于10.0,它实际上被认为是1乘以10的幂(即10^1),所以它使用科学计数法的结构。
解法
用 BigDicemal 的 toPlainString() 方法替代
public String toPlainString() {
if(scale==0) {
if(intCompact!=INFLATED) {
return Long.toString(intCompact);
} else {
return intVal.toString();
}
}
if(this.scale<0) { // No decimal point
if(signum()==0) {
return "0";
}
int tailingZeros = checkScaleNonZero((-(long)scale));
StringBuilder buf;
if(intCompact!=INFLATED) {
buf = new StringBuilder(20+tailingZeros);
buf.append(intCompact);
} else {
String str = intVal.toString();
buf = new StringBuilder(str.length()+tailingZeros);
buf.append(str);
}
for (int i = 0; i < tailingZeros; i++)
buf.append('0');
return buf.toString();
}
String str ;
if(intCompact!=INFLATED) {
str = Long.toString(Math.abs(intCompact));
} else {
str = intVal.abs().toString();
}
return getValueString(signum(), str, scale);
}
- 判断是否有小数部分
if(scale==0) {
if(intCompact!=INFLATED) {
return Long.toString(intCompact);
} else {
return intVal.toString();
}
}
- 如果
scale为 0,表示没有小数部分。 - 如果
intCompact不是INFLATED(可能表示它是一个较小的整数),则直接将其转换为字符串返回。 - 否则使用
intVal的字符串表示返回。
- 处理没有小数点的情况
if(this.scale<0) { // No decimal point
if(signum()==0) {
return "0";
}
int tailingZeros = checkScaleNonZero((-(long)scale));
StringBuilder buf;
if(intCompact!=INFLATED) {
buf = new StringBuilder(20+tailingZeros);
buf.append(intCompact);
} else {
String str = intVal.toString();
buf = new StringBuilder(str.length()+tailingZeros);
buf.append(str);
}
for (int i = 0; i < tailingZeros; i++)
buf.append('0');
return buf.toString();
}
- 如果
scale为负数,表示没有小数点且有多个尾随零。 - 如果数值的符号是 0,直接返回 "0"。 - 调用checkScaleNonZero方法确定需要补充的零的数量。 - 创建一个字符缓冲区buf,根据intCompact的值选择初始化字符串长度。 - 将实际数值(可能是长整型或者大整数)添加到buf中。 - 在buf后面追加必要的零。 - 最后返回构建的字符串。
- 处理有小数点的情况
String str ;
if(intCompact!=INFLATED) {
str = Long.toString(Math.abs(intCompact));
} else {
str = intVal.abs().toString();
}
return getValueString(signum(), str, scale);
```
- 如果
scale大于 0,表示数值有小数点。 - 判断
intCompact是否是INFLATED,选择获取数值的绝对值字符串。 - 通过调用
getValueString方法传入符号、绝对值字符串和小数位数来生成最终的字符串表示。
总结
BigDecimal.toPlainString() 这个方法的作用是根据数值的符号、整数和小数部分的不同情况,生成一个纯文本格式的字符串表示。这是一个典型的数值转换方法,适用于需要精确表示数值(如财务数据)的场合,不使用科学计数法形式。