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();
    }

### 处理Decimal数据类型溢出导致的科学计数法表示 当 `decimal` 数据类型的数值超出其定义范围时,默认情况下可能会被转换成科学计数法来表示。为了避免这种情况的发生,可以采取多种方法: #### 方法一:调整显示格式 通过指定输出格式的方式,强制将数字以常规形式展示而不是科学记数法。 对于SQL Server环境下的查询语句中遇到此问题的情况,可以通过如下方式实现: ```sql SELECT CAST(CAST(字段 AS DECIMAL(38, 0)) AS VARCHAR(50)) ``` 这种方式先将原始值转化为高精度的 `DECIMAL` 类型再转为字符串,从而避免了自动采用科学计数法的可能性[^1]。 #### 方法二:使用Java中的BigDecimal类处理大额货币量或其他重要数值 在编程语言层面,比如 Java 中处理财务计算等场景下经常使用的 `BigDecimal` 对象不会轻易出现因存储空间不足而产生的溢出现象,并且提供了 `.toPlainString()` 方法用于返回不带指数部分的标准字符串表达形式。 ```java System.out.println(amount.getInitTotalAmount().toPlainString()); // 如果金额为12000000,则输出的结果会保持原样即 "12000000" ``` 这种方法适用于需要严格控制数值呈现的应用程序开发过程中,在确保准确性的同时也解决了潜在的数据丢失风险[^4]。 #### 方法三:考虑浮点数精度局限性并合理规划变量声明 值得注意的是,如果涉及到更广泛的数值运算而非仅仅关注于最终结果显示的话,还需要注意到不同计算机系统内部对各种基础数据类型的特殊规定及其相互之间转换所带来的影响。特别是像 `float`, `double` 这样的浮点数类型存在固有的舍入误差问题,这可能导致预期之外的行为。所以在设计之初就应当充分考虑到这一点,尽可能选用合适的数据结构去匹配实际需求[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值