ASP .net C# Decimal 与 Java Double 之间的舍入不匹配
Rounding mismatch between ASP .net C# Decimal to Java Double
我正在 运行将 .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
,这是预期的。
The conversion/translation of the function to Java is as follows. I din't find exact
Math.Round
option.
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
,这是不正确的。
我什至尝试使用此处 C# Double Rounding 中的参考,在 Java 中使用 BigDecimal
修改代码,但没有成功。
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.
完全匹配
问题来自双精度与小数在内存中的存储和表示方式。有关详细信息,请参阅以下链接:Doubles Decimals
让我们看看它们在您的代码中是如何工作的。使用双打,参数为 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);
如果函数必须取入并且return加倍:
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();
}
请记住,双精度不能完全表示小数可以表示的相同值。因此,returning a double 函数不能具有与 returns 小数的原始 C# 函数完全相同的输出。
这里真正的问题是 Math.round 有两个定义。一个 return 是 long,而另一个 return 是 int!当您提供双倍时,它 运行 会持续很长时间。要解决此问题,只需将您的输入转换为浮点数,使其 运行 成为 return 整数。
double numberDecimalMultiplier = Math.round((float)(number / roundPrecision));
我正在 运行将 .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
,这是预期的。
The conversion/translation of the function to Java is as follows. I din't find exact
Math.Round
option.
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
,这是不正确的。
我什至尝试使用此处 C# Double Rounding 中的参考,在 Java 中使用 BigDecimal
修改代码,但没有成功。
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.
完全匹配问题来自双精度与小数在内存中的存储和表示方式。有关详细信息,请参阅以下链接:Doubles Decimals
让我们看看它们在您的代码中是如何工作的。使用双打,参数为 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);
如果函数必须取入并且return加倍:
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();
}
请记住,双精度不能完全表示小数可以表示的相同值。因此,returning a double 函数不能具有与 returns 小数的原始 C# 函数完全相同的输出。
这里真正的问题是 Math.round 有两个定义。一个 return 是 long,而另一个 return 是 int!当您提供双倍时,它 运行 会持续很长时间。要解决此问题,只需将您的输入转换为浮点数,使其 运行 成为 return 整数。
double numberDecimalMultiplier = Math.round((float)(number / roundPrecision));