我正在将.NET代码转换为Java并遇到精度不匹配的问题.
.NET代码:
private decimal roundToPrecision(decimal number, decimal roundPrecision) { if (roundPrecision == 0) return number; decimal numberDecimalMultiplier = Math.Round(number / roundPrecision, MidpointRounding.AwayFromZero); return numberDecimalMultiplier * roundPrecision; }
roundToPrecision(8.7250, 0.05);
上面代码中的调用函数给出了我8.75
期望的函数.
将函数转换/转换为Java如下.我找不到确切的
Math.Round
选择.
Java代码:
public double roundToPrecision(double number, double roundPrecision) { if (roundPrecision == 0) return number; int len = Double.toString(roundPrecision).split("\\.")[1].length(); double divisor = 0d; switch (len) { case 1: divisor = 10d; break; case 2: divisor = 100d; break; case 3: divisor = 1000d; break; case 4: divisor = 10000d; break; } double numberDecimalMultiplier = Math.round(number / roundPrecision); double res = numberDecimalMultiplier * roundPrecision; return Math.round(res * divisor) / divisor; }
调用roundToPrecision(8.7250, 0.05);
Java代码给了我8.7
这个并不正确.
我甚至尝试BigDecimal
在Java中使用引用修改代码如下C#Double Rounding但没有运气.
public double roundToPrecision(double number, double roundPrecision) { if (roundPrecision == 0) return number; int len = Double.toString(roundPrecision).split("\\.")[1].length(); double divisor = 0d; switch (len) { case 1: divisor = 10d; break; case 2: divisor = 100d; break; case 3: divisor = 1000d; break; case 4: divisor = 10000d; break; } BigDecimal b = new BigDecimal(number / roundPrecision); b = b.setScale(len,BigDecimal.ROUND_UP); double numberDecimalMultiplier = Math.round(b.doubleValue()); double res = numberDecimalMultiplier * roundPrecision; return Math.round(res * divisor) / divisor; }
请指导我为解决这个问题需要做些什么.
以下是一些尝试的方案.
number = 10.05
; precision = .1
; 预期= 10.1
;
number = 10.12
; precision = .01
; 预期= 10.12
;
number = 8.7250
; precision = 0.05
; 预期= 8.75
;
number = 10.999
; precision = 2
; 预期= 10
;
number = 6.174999999999999
; precision = 0.05
; 预期= 6.20
;
注意:我有超过6万个数字,精度可以从1位小数到4位小数不等..NET的输出应该与Java完全匹配.
问题来自于如何在内存中存储和表示双精度数与小数数.有关更多细节,请参阅以下链接:双打 小数
我们来看看它们在代码中的工作方式.使用双精度,参数为8.725和0.05. number / roundPrecision
给出174.499...
,因为双打不能完全代表174.5.随着小数number / roundPrecision
给人174.5
,小数都能够准确地表示这一点.所以当174.499...
得到四舍五入时,它会向下舍入174
而不是175
.
使用BigDecimal
是朝着正确方向迈出的一步.但是如何在代码中使用它是一个问题.当您创建BigDecimal值时会出现问题.
BigDecimal b = new BigDecimal(number / roundPrecision);
它BigDecimal
是从双重创建的,因此不精确已经存在.如果你能够BigDecimal
从一个更好的字符串创建参数.
public static BigDecimal roundToPrecision(BigDecimal number, BigDecimal roundPrecision) { if (roundPrecision.signum() == 0) return number; BigDecimal numberDecimalMultiplier = number.divide(roundPrecision, RoundingMode.HALF_DOWN).setScale(0, RoundingMode.HALF_UP); return numberDecimalMultiplier.multiply(roundPrecision); } BigDecimal n = new BigDecimal("-8.7250"); BigDecimal p = new BigDecimal("0.05"); BigDecimal r = roundToPrecision(n, p);
如果函数必须接受并返回双精度数:
public static double roundToPrecision(double number, double roundPrecision) { BigDecimal numberBig = new BigDecimal(number). setScale(10, BigDecimal.ROUND_HALF_UP); BigDecimal roundPrecisionBig = BigDecimal.valueOf(roundPrecision); if (roundPrecisionBig.signum() == 0) return number; BigDecimal numberDecimalMultiplier = numberBig.divide(roundPrecisionBig, RoundingMode.HALF_DOWN).setScale(0, RoundingMode.HALF_UP); return numberDecimalMultiplier.multiply(roundPrecisionBig).doubleValue(); }
请记住,双精度数不能精确地表示小数可以表示的相同值.因此返回double的函数不能具有返回小数的原始C#函数的确切输出.