如何将 double 截断为 N 位小数?
How to truncate double to N decimal places?
我想将我的 double 值四舍五入到小数点后 N 位(比如一位),基本上只是忽略后面的所有数字:
0.123 #=> 0.1
0.19 #=> 0.1
0.2 #=> 0.2
这个问题已经被多次提出,例如here and here。推荐的方法是使用 BigDecimal
然后缩放它,特别是为了避免昂贵的字符串转换和返回。我需要的舍入模式显然是 RoundingMode.DOWN
.
所以方法是这样的:
static double truncate(double value, int places) {
return new BigDecimal(value)
.setScale(places, RoundingMode.DOWN)
.doubleValue();
}
但是,由于精度的损失,它returns有些出乎意料的结果:
truncate(0.2, 1) #=> 0.2
truncate(0.3, 1) #=> 0.2
truncate(0.4, 1) #=> 0.4
truncate(0.2, 3) #=> 0.2
truncate(0.3, 3) #=> 0.299
truncate(0.4, 3) #=> 0.4
这有两个问题:
0.3
应该如何工作?为什么在这种情况下会损失精度?它不会破坏 BigDecimal
的全部目的吗?
如何正确截断我的值?
谢谢。
将数字乘以 pow(10,n)
并以整数类型存储,然后再除以 pow(10,n)
将 double
截断为 N 位小数并不是一个真正有意义的问题,因为简单的十进制数(例如 0.3
)不能表示为 double
。
如果你想知道 double
0.3
的真实价值,你可以
System.out.println(new BigDecimal(0.3));
你会发现它真的是
0.299999999999999988897769753748434595763683319091796875
所以 0.299
是正确答案。
这个问题只有使用 BigDecimal
并以 BigDecimal
或 String
而非 double
给出答案才真正有意义。 @user1886323 的回答显示了如何做到这一点。
"in particular to avoide the expensive conversion to String and back" - 这正是避免精度损失的原因。您无法免费获得任意精度。
如果你需要任意精度那么你不应该使用 double
而是做:
static String truncate(String value, int places) {
return new BigDecimal(value)
.setScale(places, RoundingMode.DOWN)
.stripTrailingZeros()
.toString()
}
我知道这是一个古老的post,但我只是想出了一个简单的解决方案:
有第二个双精度数,它接收要截断的数字的模数,然后从第一个数中减去它。示例(截断为一位数)
double original = 23.875114784205696;
double truncater = original % 0.1;
//truncater now equals 0.075114784205696;
original - truncater = 23.8;
显然这可以扩展到任何需要截断的地方,虽然它可能不会缩小内存分配中的双倍,但它会为了显示目的切断结尾。
我想将我的 double 值四舍五入到小数点后 N 位(比如一位),基本上只是忽略后面的所有数字:
0.123 #=> 0.1
0.19 #=> 0.1
0.2 #=> 0.2
这个问题已经被多次提出,例如here and here。推荐的方法是使用 BigDecimal
然后缩放它,特别是为了避免昂贵的字符串转换和返回。我需要的舍入模式显然是 RoundingMode.DOWN
.
所以方法是这样的:
static double truncate(double value, int places) {
return new BigDecimal(value)
.setScale(places, RoundingMode.DOWN)
.doubleValue();
}
但是,由于精度的损失,它returns有些出乎意料的结果:
truncate(0.2, 1) #=> 0.2
truncate(0.3, 1) #=> 0.2
truncate(0.4, 1) #=> 0.4
truncate(0.2, 3) #=> 0.2
truncate(0.3, 3) #=> 0.299
truncate(0.4, 3) #=> 0.4
这有两个问题:
0.3
应该如何工作?为什么在这种情况下会损失精度?它不会破坏BigDecimal
的全部目的吗?如何正确截断我的值?
谢谢。
将数字乘以 pow(10,n)
并以整数类型存储,然后再除以 pow(10,n)
将 double
截断为 N 位小数并不是一个真正有意义的问题,因为简单的十进制数(例如 0.3
)不能表示为 double
。
如果你想知道 double
0.3
的真实价值,你可以
System.out.println(new BigDecimal(0.3));
你会发现它真的是
0.299999999999999988897769753748434595763683319091796875
所以 0.299
是正确答案。
这个问题只有使用 BigDecimal
并以 BigDecimal
或 String
而非 double
给出答案才真正有意义。 @user1886323 的回答显示了如何做到这一点。
"in particular to avoide the expensive conversion to String and back" - 这正是避免精度损失的原因。您无法免费获得任意精度。
如果你需要任意精度那么你不应该使用 double
而是做:
static String truncate(String value, int places) {
return new BigDecimal(value)
.setScale(places, RoundingMode.DOWN)
.stripTrailingZeros()
.toString()
}
我知道这是一个古老的post,但我只是想出了一个简单的解决方案:
有第二个双精度数,它接收要截断的数字的模数,然后从第一个数中减去它。示例(截断为一位数)
double original = 23.875114784205696;
double truncater = original % 0.1;
//truncater now equals 0.075114784205696;
original - truncater = 23.8;
显然这可以扩展到任何需要截断的地方,虽然它可能不会缩小内存分配中的双倍,但它会为了显示目的切断结尾。