java中float和double为什么会转为科学记数法?

1、背景

在日常开发中有时会使用到float或double数据类型,然而在前端接收到数据时发现数据为科学记数法,此时不能满足业务显示直观的需求。为什么float和double会变成科学记数法格式呢?什么情况下会自动转换?

2、思考

带着上面的两个问题,我们一起撸撸源码吧。

(1) Float类对数据赋值和显示时会调用toString()方法:

public static String toString(float f) {
        return FloatingDecimal.toJavaFormatString(f);
    }

(2) 在FloatingDecimal类中调用toJavaFormatString()时,需要调用转换器:

public static String toJavaFormatString(float var0) {
        return getBinaryToASCIIConverter(var0).toJavaFormatString();
}

(3) 其中BinaryToASCIIConverter接口的toJavaFormatString()方法实现如下:

public String toJavaFormatString() {
    int var1 = this.getChars(this.buffer);
    return new String(this.buffer, 0, var1);
}

(4) 在getChars()方法中,float和double完成了科学记数法的变形

if (this.decExponent > 0 && this.decExponent < 8) {
    var3 = Math.min(this.nDigits, this.decExponent);
    System.arraycopy(this.digits, this.firstDigitIndex, var1, var2, var3);
    var5 = var2 + var3;
    if (var3 < this.decExponent) {
        var3 = this.decExponent - var3;
        Arrays.fill(var1, var5, var5 + var3, 0);
        var5 += var3;
        var1[var5++] = .;
        var1[var5++] = 0;
    } else {
        var1[var5++] = .;
        if (var3 < this.nDigits) {
            int var4 = this.nDigits - var3;
            System.arraycopy(this.digits, this.firstDigitIndex + var3, var1, var5, var4);
            var5 += var4;
        } else {
            var1[var5++] = 0;
        }
    }
} else if (this.decExponent <= 0 && this.decExponent > -3) {
    var5 = var2 + 1;
    var1[var2] = 0;
    var1[var5++] = .;
    if (this.decExponent != 0) {
        Arrays.fill(var1, var5, var5 - this.decExponent, 0);
        var5 -= this.decExponent;
    }

    System.arraycopy(this.digits, this.firstDigitIndex, var1, var5, this.nDigits);
    var5 += this.nDigits;
} else {
    var5 = var2 + 1;
    var1[var2] = this.digits[this.firstDigitIndex];
    var1[var5++] = .;
    if (this.nDigits > 1) {
        System.arraycopy(this.digits, this.firstDigitIndex + 1, var1, var5, this.nDigits - 1);
        var5 += this.nDigits - 1;
    } else {
        var1[var5++] = 0;
    }

    var1[var5++] = E;
    if (this.decExponent <= 0) {
        var1[var5++] = -;
        var3 = -this.decExponent + 1;
    } else {
        var3 = this.decExponent - 1;
    }

    if (var3 <= 9) {
        var1[var5++] = (char)(var3 + 48);
    } else if (var3 <= 99) {
        var1[var5++] = (char)(var3 / 10 + 48);
        var1[var5++] = (char)(var3 % 10 + 48);
    } else {
        var1[var5++] = (char)(var3 / 100 + 48);
        var3 %= 100;
        var1[var5++] = (char)(var3 / 10 + 48);
        var1[var5++] = (char)(var3 % 10 + 48);
    }
}

在此部分代码中,分为三个大部分进行了转换:小数点前的位数大于0位但小于8位,数值为小于1且小数点紧跟的0小于3个,小数点前位数大于等于8位或纯小数的小数点后的0大于等于3。

简而言之,当数据为12345678或者0.0001222类型时,float或double数据会自动转换为科学记数法格式。

3、解决方法

(1) 数据采用BigDecimal格式

(2) NumberFormat

public static void main(String[] args) {
        double d = 123456789.128d;
        String s1 = big(d);
        String s2 = big2(d);
        System.out.println(d);
        System.out.println(s1);
        System.out.println(s2);
    }

// 方法一:NumberFormat
private static String big(double d) {
     NumberFormat nf = NumberFormat.getInstance();
        // 是否以逗号隔开, 默认true以逗号隔开,如[123,456,789.128]
        nf.setGroupingUsed(false);
        // 结果未做任何处理
        return nf.format(d);
    }

//方法二: BigDecimal
private static String big2(double d) {
        BigDecimal d1 = new BigDecimal(Double.toString(d));
        BigDecimal d2 = new BigDecimal(Integer.toString(1));
        // 四舍五入,保留2位小数
        return d1.divide(d2,2,BigDecimal.ROUND_HALF_UP).toString();
    }
经验分享 程序员 微信小程序 职场和发展