Java删除导致.0变为.99

问题描述

我只想简单地将一个具有小数位数的双精度数字转换为4个小数位数而不四舍五入的函数。 我的这段代码运行正常,但是找到了一个随机实例,将.0转换为.99 这是一些示例输出

4.12897456 ->4.1289
4.5 ->4.5
4.5231->4.5231
5.53->5.53
5.52->5.199 (Wrong conversion,I want it to be 5.52)

    private static double get4Donly(double val){
    double converted = ((long)(val * 1e4)) / 1e4;
    return converted
    }

编辑:这种转换被调用了数千次,所以请提出一种我不必一直创建新字符串的方法

解决方法

您可以使用DecimalFormat

import java.text.DecimalFormat;
import java.math.RoundingMode;
import java.util.Arrays;
public class MyClass {
    public static void main(String args[]) {
        DecimalFormat df = new DecimalFormat("#.####");
        df.setRoundingMode(RoundingMode.DOWN);
        for (Number n : Arrays.asList(4.12897456,4.5,4.5231,5.53,5.52)) {
            Double d = n.doubleValue();
            System.out.println(df.format(d));
        }
    }
}

RoundingMode.DOWN舍入为零,new DecimalFormat("#.####")创建一个DecimalFormat实例,该实例将数字格式化为最多4个小数位。将两者放在一起,上面的代码将产生以下输出,我相信这符合您的期望:

4.1289

4.5

4.5231

5.53

5.52

,

我建议通过将double转换为String来使用.substring()方法。由于您不需要四舍五入,因此更容易理解和实现。

此外,它是所有其他方法中最简单的方法,例如使用DecimalFormat

在这种情况下,您可以这样做:

private static double get4Donly(double val){
    String num = String.valueOf(val);
    return Double.parseDouble(num.substring(0,6));
}

但是,如果结果的长度小于6个字符,则可以执行以下操作:

private static double get4Donly(double val){
    String num = String.valueOf(val);
    if(num.length()>6) {
        return Double.parseDouble(num.substring(0,6));
    }else {
        return val;
    }
    
}
,

双打并不像您认为的那样起作用。

它们以二进制形式而不是十进制形式存储。就像'1除以3'不能用十进制双精度表示(0.3333333333是不够的,它是无限的3s,所以不能表示,因此会产生舍入误差),但是'1除以5'是可以表示的很好,有些数字是可表示的,并且在以double类型存储时,这些数字最终会四舍五入,但至关重要的是,十进制看上去完全可以舍入的东西可能无法用二进制进行舍入。

鉴于它们不匹配,您的想法是“嗯,我将乘以4,将其变为长整数,然后再转换为双精度数,然后除以1000”不会让这些数字消失通过毫不留情。这不是您四舍五入的方法,因为除了使用双打已经开始造成的损失之外,您还引入了其他损失。

您有3种解决方案:

只需正确打印

双精度数不能认为是“小数点后有4位数字”,因为双精度数不是十进制数。

因此,说这句话甚至都没有道理:请将此双数取整至最多4个小数位数。

那是至关重要的认识。一旦您了解到您会顺利进行的:)

您可以做的是“请取这双并用小数点后的不超过4位数字打印出来。”

String out = String.format("%.4f",5.52);

或者您可以使用System.printf(XXX)的缩写System.print(String.format(XXX))

这可能就是您想要的

忘记完全翻倍

对于某些领域,最好放弃双打并改用多头或整数。例如,如果您要进行财务业务,最好将每种货币的原子单位长期存储,而放弃双倍。因此,以美元计,可以长期存储美分。对于欧元,相同。对于比特币,请存储satoshis。编写自定义渲染,以适合该货币的形式渲染回去:

long value = 450; // $4.50

String formatCurrency(long cents) {
    return String.format("%s%s%d.%02d",cents < 0 ? "-" : " ","$",Math.abs(cents) / 100,Math.abs(cents) % 100);
}

使用BigDecimal

这通常比麻烦多得多,但是它以十进制存储每个数字-它代表十进制表示法可以做到的所有事情(而且它也不能代表其他任何东西-BigDecimal中不可能用1除以3)。