2017-01-13 5 views
11

.NETコードをJavaに翻訳していて、精度が不足していて問題が一致しませんでした。ゼロからの中点丸め.net C#十進数からJava double

.NETコード:上記のコードでroundToPrecision(8.7250, 0.05);関数を呼び出す

private decimal roundToPrecision(decimal number, decimal roundPrecision) 
{ 
    if (roundPrecision == 0) 
     return number; 
    decimal numberDecimalMultiplier = Math.Round(number/roundPrecision, MidpointRounding.AwayFromZero); 
    return numberDecimalMultiplier * roundPrecision; 
} 

は私に期待されている8.75を与えます。

Javaへの変換/変換は、次のとおりです。私は正確に Math.Roundオプションを見つけることができません。

Javaコード:JavaコードでroundToPrecision(8.7250, 0.05);を呼び出す

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; 
} 

は私に8.7を与え、これは正しくありません。

BigDecimalでコードを修正しようとしましたが、ここでは参照番号C# Double Roundingを使って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; 
    } 
    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;

注:私には60,000を超える数値があり、精度は小数点以下1桁から4桁まで変化します。 .NETの出力は、Javaと正確に一致する必要があります。

答えて

5

問題は、倍数と小数点がメモリに格納され、どのように表現されるかに起因します。詳細については、次のリンクを参照してください。DoublesDecimals

それぞれのコードでどのように動作するかを見てみましょう。 doubleを使う。引数は8.725と0.05です。 number/roundPrecision174.499...となります。ダブルは正確に174.5を表すことができないためです。小数でnumber/roundPrecision174.5を与える場合、小数はこれを正確に表すことができます。したがって、174.499...が丸められた場合、175の代わりに174に切り下げられます。

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#関数と同じ正確な出力を持つことはできません。

+0

ありがとうございます。しかし、私はすでにこれを試しました。シナリオ1と2(私は質問を更新しました)を試してみてください。彼らはあなたの実装に期待される結果を与えません。 –

+0

あなたのソリューションは 'java.lang.ArithmeticException:非終動小数点の展開をスローします。数値が10.0で精度が0.1の場合、BigDecimalの数値表現の正確な10進数の結果は表示されません。 –

+0

例外をスローするときの引数は何ですか? – gunnerone

0

実際の問題は、Math.roundに2つの定義があることです。一方はlongを返し、もう一方はintを返します。あなたがダブルを提供すると、それは長い間それを実行します。これを修正するには、入力をfloatにキャストし、intを返すように入力をキャストします。

double numberDecimalMultiplier = Math.round((float)(number/roundPrecision)); 
関連する問題