4

在将算法从 JavaScript 移植到 Java 时,我遇到了需要替换 JavaScript 的 toPrecision() 的问题。问题是我不知道数字有多大或多小,所以我不能使用具有正确格式的简单 NumberFormat 。

是否有提供类似功能的标准类?

编辑这是我想出的:

   double toPrecision(double n, double p) {
        if (n==0) return 0;

        double e = Math.floor(Math.log10(Math.abs(n)));
        double f = Math.exp((e-p+1)*Math.log(10));

        return Math.round(n/f)*f;
    }

原则上,它做正确的事,但舍入错误完全毁了它。例如, toPrecision(12.34567, 3)返回12.299999999999997

编辑 2 这个版本非常适用于 12 个测试用例中的 11 个......

   double toPrecision(double n, double p) {
        if (n==0) return 0;

        double e = Math.floor(Math.log10(Math.abs(n)));
        double f = Math.round(Math.exp((Math.abs(e-p+1))*Math.log(10)));
        if (e-p+1<0) {
            f = 1/f;
        }

        return Math.round(n/f)*f;
    }

toPrecision(0.00001234567, 3)仍然返回1.2299999999999999E-5而不是1.23E-5

4

6 回答 6

12

设置精度的用途BigDecimalsetScale()方法

BigDecimal bd = new BigDecimal("1.23456789");
System.out.println(bd.setScale(3,BigDecimal.ROUND_HALF_UP));

输出

1.235

于 2012-08-21T11:41:12.670 回答
3

我为此想出的最简单的解决方案是使用java.math.BigDecimaljava.math.MathContext类似的组合。

String toPrecision(double number, int precision) {
    return new BigDecimal(number, new MathContext(precision)).toString();
}

我在 Number.prototype.toPrecision 的dynjs 实现中使用

于 2012-11-13T17:11:59.767 回答
2

这是一个使用 String.format 的 java 解决方案。

public static String toPrecision(double d, int digits) {
    s = String.format("%."+((digits>0)?digits:16)+"g",d).replace("e+0","e+").replace("e-0","e-");
    return s;
}

.replace 仅在您想模仿指数没有前导零的 javascript 时才需要。如果您只是将其用于舍入,则将值返回为

return Double.parseDouble(s);

这是一些单元测试代码:

public void testToPrecision() {
    String s = NumberFormat.toPrecision(1234567.0,5);
    assertEquals("1.2346e+6",s);
    s = NumberFormat.toPrecision(12.34567,5);
    assertEquals("12.346",s);
    s = NumberFormat.toPrecision(0.1234567,5);
    assertEquals("0.12346",s);
    s = NumberFormat.toPrecision(0.1234567e20,5);
    assertEquals("1.2346e+19",s);
    s = NumberFormat.toPrecision(-0.1234567e-8,5);
    assertEquals("-1.2346e-9",s);
    s = NumberFormat.toPrecision(1.0/3.0,5);
    assertEquals("0.33333",s);
    s = NumberFormat.toPrecision(1.0/3.0,0);
    assertEquals("0.3333333333333333",s);
}
于 2012-11-20T18:14:28.840 回答
1

你可以double使用

double d = 1.23456789;
System.out.println(Math.round(d * 1e3) / 1e3);

印刷

1.235

或者

System.out.printf("%.3f%n", d);

做同样的事情。


public static void main(String... args) {
    System.out.println(round3significant(12345678.9));
    System.out.println(round3significant(0.0000012345));
}

public static double round3significant(double d) {
    if (d < 100) {
        double divide = 1;
        while(d < 100) {
            d *= 10;
            divide *= 10;
        }
        return Math.round(d) / divide;
    } else {
        double multi = 1;
        while(d > 1000) {
            d /= 10;
            multi *= 10;
        }
        return Math.round(d) * multi;
    }
}

印刷

1.23E7
1.23E-6

您可以使用 NumberFormat 仅显示为小数。

于 2012-08-21T11:51:07.003 回答
1

这终于奏效了...

double toPrecision(double n, double p) {
    if (n==0) return 0;

    double e = Math.floor(Math.log10(Math.abs(n)));
    double f = Math.round(Math.exp((Math.abs(e-p+1))*Math.log(10)));

    if (e-p+1<0) {
        return Math.round(n*f)/f;
    }

    return Math.round(n/f)*f;
}
于 2012-08-21T12:44:41.110 回答
0
    import java.text.*;  
Class Decimals
{  
    public static void main(String[] args)
    {  
        float f = 125.069f;  
        DecimalFormat form = new DecimalFormat("#.##");  
        System.out.println(form.format(f));  
    }
}

.## 代表您想要的小数位数我希望这符合您的要求。

于 2012-08-21T12:09:27.673 回答