在Java中使双精度圆化
我找到了一个很好的四舍五入解决方案:在Java中使双精度圆化,java,floating-point,Java,Floating Point,我找到了一个很好的四舍五入解决方案: static Double round(Double d, int precise) { BigDecimal bigDecimal = new BigDecimal(d); bigDecimal = bigDecimal.setScale(precise, RoundingMode.HALF_UP); return bigDecimal.doubleValue(); } 然而,结果令人困惑: System.out.println(
static Double round(Double d, int precise) {
BigDecimal bigDecimal = new BigDecimal(d);
bigDecimal = bigDecimal.setScale(precise, RoundingMode.HALF_UP);
return bigDecimal.doubleValue();
}
然而,结果令人困惑:
System.out.println(round(2.655d,2)); // -> 2.65
System.out.println(round(1.655d,2)); // -> 1.66
为什么它会给出这个输出?我正在使用jre 1.7.0_45。如中所述
这是因为它不能准确地表示双值。因此您必须使用
BigDecimal BigDecimal=BigDecimal.valueOf(d)代码>而不是BigDecimal BigDecimal=新的BigDecimal(d)代码>十进制数字不能用双精度表示
因此,2.655最终是:
2.6549999999980460074766597
而1.655最终是:
1.655000000000000026645352591将double
respdouble
四舍五入本身没有多大意义,因为double
数据类型不能四舍五入(很容易,或者根本不可能)
您正在做的是:
将一个双d
作为输入,并在分隔符后面输入一个int-precision
位数
从该d
创建一个BigDecimal
将BigDecimal
四舍五入
返回该BigDecimal
的double
值,该值不再应用舍入
你可以走两条路:
您可以返回一个表示四舍五入双精度的BigDecimal
,然后决定如何处理它
您可以返回一个表示四舍五入的BigDecimal
的字符串
这两种方法中的任何一种都是有意义的。最终都是不言自明的:
public static void main (String[] args) throws java.lang.Exception
{
System.out.println("Rounded: " + round(2.655d,2)); // -> 2.65
System.out.println("Rounded: " + round(1.655d,2)); // -> 1.66
}
public static Double round(Double d, int precise)
{
BigDecimal bigDecimal = new BigDecimal(d);
System.out.println("Before round: " + bigDecimal.toPlainString());
bigDecimal = bigDecimal.setScale(precise, RoundingMode.HALF_UP);
System.out.println("After round: " + bigDecimal.toPlainString());
return bigDecimal.doubleValue();
}
输出:
Before round: 2.654999999999999804600747665972448885440826416015625
After round: 2.65
Rounded: 2.65
Before round: 1.6550000000000000266453525910037569701671600341796875
After round: 1.66
Rounded: 1.66
要解决这个问题,一个肮脏的方法是:
这里,15
是双精度在基数10中可以表示的最大位数以下的一位。输出:
Before round: 2.654999999999999804600747665972448885440826416015625
Hack round: 2.655000000000000
After round: 2.66
Rounded: 2.66
Before round: 1.6550000000000000266453525910037569701671600341796875
Hack round: 1.655000000000000
After round: 1.66
Rounded: 1.66
你必须更换
BigDecimal bigDecimal = new BigDecimal(d);
与
您将获得预期的结果:
2.66
1.66
Java API的解释:
BigDecimal.valueOf(double val)使用double.toString()方法提供的double的规范字符串表示形式。这是将double(或float)转换为BigDecimal的首选方法
新的BigDecimal(double val)-使用双精度二进制浮点值的精确十进制表示,因此此构造函数的结果可能有些不可预测。您可以尝试如下更改程序:-
static Double round(Double d, int precise)
{
BigDecimal bigDecimal = BigDecimal.valueOf(d);
bigDecimal = bigDecimal.setScale(precise, RoundingMode.HALF_UP);
return bigDecimal.doubleValue();
}
Success time: 0.07 memory: 381184 signal:0
Rounded: 2.66
Rounded: 1.66
Success time: 0.07 memory: 381248 signal:0
Rounded: 2.66
Rounded: 1.66
使用BigDecimal.valueOf
而不是使用new BigDecimal
获得预期结果的原因,用以下文字表示:
BigDecimal.valueOf(double)
将使用传入的double值的值来实例化BigDecimal对象。换句话说,BigDecimal
对象的值将是执行System.out.println(d)
时所看到的值
但是,如果使用新的BigDecimal(d)
,则BigDecimal将尽可能准确地表示双精度值。这通常会导致存储的数字比您想要的多得多
因此会导致您在节目中看到的一些混乱
从Java文档:
-使用双精度的规范字符串表示法将双精度转换为BigDecimal
由Double.toString(Double)方法提供
-
将double转换为BigDecimal,即精确的十进制
表示双精度二进制浮点值。天平
返回的BigDecimal的最小值为(10scale×)
val)是一个整数。注:
- 此构造函数的结果可能有些不可预测。有人可能会认为,在Java中编写新的BigDecimal(0.1)会创建一个
BigDecimal正好等于0.1(未标度值为1,
比例为1),但实际上等于
0.1000000000000000055511151231257827021181583404541015625. 这是因为0.1不能精确地表示为双精度(或者,对于双精度)
物质,作为任何有限长度的二元分数)。因此,值
传递给构造函数的不完全等于
0.1,尽管有外观
- 另一方面,字符串构造函数是完全可预测的:编写新的BigDecimal(“0.1”)将创建一个BigDecimal
这正好等于0.1,正如人们所期望的。因此,
通常建议在中使用字符串构造函数
我更喜欢这个
- 当必须将double用作BigDecimal的源时,请注意此构造函数提供了精确的转换;它不会给出
与使用
将double转换为字符串的结果相同
Double.toString(Double)方法,然后使用BigDecimal(String)
构造器。要获得该结果,请使用静态值(dou
static Double round(Double d, int precise)
{
BigDecimal bigDecimal = BigDecimal.valueOf(d);
bigDecimal = bigDecimal.setScale(precise, RoundingMode.HALF_UP);
return bigDecimal.doubleValue();
}
Success time: 0.07 memory: 381184 signal:0
Rounded: 2.66
Rounded: 1.66
Success time: 0.07 memory: 381248 signal:0
Rounded: 2.66
Rounded: 1.66