我正在将.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;
}在Java代码中调用roundToPrecision(8.7250, 0.05);会给我8.7,这是不正确的。
我甚至尝试用BigDecimal修改代码,如下所示,使用这里的引用,C#双舍入,但没有成功。
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;
}请指导我,我需要做什么来解决这个问题。
下面是几种需要尝试的场景。
10.05;精度= .1;期望= 10.1;10.12;精度= .01;期望= 10.12;8.7250;精度= 0.05;期望= 8.75;10.999;精度= 2;期望= 10;6.174999999999999;精度= 0.05;期望= 6.20;注:我有6万多个数字,精度可以从小数点1位到小数点4位不等。.NET的输出应该与Java完全匹配。
发布于 2017-01-13 23:40:40
问题在于双小数和小数是如何存储和表示在内存中的。有关更多细节,请参见这些链接:双打 小数
让我们看看它们在您的代码中是如何工作的。使用双数,参数为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();
}请记住,双倍不能完全表示小数所能表示的相同值。因此,返回双的函数不能像返回小数的原始C#函数那样具有精确的输出。
发布于 2017-01-24 02:50:13
这里真正的问题是Math.round有两个定义。一个返回一个长,另一个返回一个int!当你提供一个双倍,它运行的时间很长。要解决这个问题,只需将输入转换为浮点数,让它运行返回int的输入。
double numberDecimalMultiplier = Math.round((float)(number / roundPrecision));https://stackoverflow.com/questions/41644691
复制相似问题