11

現在、私はこの方法を持っています:

static boolean checkDecimalPlaces(double d, int decimalPlaces){
    if (d==0) return true;

    double multiplier = Math.pow(10, decimalPlaces); 
    double check  =  d * multiplier;
    check = Math.round(check);      
    check = check/multiplier; 
    return (d==check);      
}

checkDecmialPlaces(649632196443.4279, 4)しかし、おそらく私が基数2の数値に基づいて基数10の計算を行うため、この方法は失敗します。

では、このチェックを正しく行うにはどうすればよいでしょうか。

double値の文字列表現を取得し、それを正規表現で確認することを考えましたが、それは奇妙に感じました。

編集: すべての答えをありがとう。私が実際にダブルを取得する場合があり、それらの場合のために私は以下を実装しました:

private static boolean checkDecimalPlaces(double d, int decimalPlaces) {
    if (d == 0) return true;

    final double epsilon = Math.pow(10.0, ((decimalPlaces + 1) * -1));

    double multiplier = Math.pow(10, decimalPlaces);
    double check = d * multiplier;
    long checkLong = (long) Math.abs(check);
    check = checkLong / multiplier;

    double e = Math.abs(d - check);
    return e < epsilon;
}

roundを切り捨てに変更しました。で行われた計算roundにより、不正確さが大きくなりすぎるようです。少なくとも失敗したテストケースでは。「実際の」文字列入力に到達できるかどうかを確認するために
使用する必要があると指摘された方もいらっしゃるので、次のようにしました。BigDecimal

BigDecimal decimal = new BigDecimal(value);
BigDecimal checkDecimal = decimal.movePointRight(decimalPlaces);
return checkDecimal.scale() == 0;

私が得る価値は、doubleExcelファイルを読み取るApachePOIAPIから得られます。いくつかのテストを行ったところ、APIはdouble数値セルの値を返しますが、すぐに次のようにフォーマットすると正確な表現を取得できることがdoubleわかりましたDecimalFormat

DecimalFormat decimalFormat = new DecimalFormat();
decimalFormat.setMaximumIntegerDigits(Integer.MAX_VALUE);
// don't use grouping for numeric-type cells
decimalFormat.setGroupingUsed(false);
decimalFormat.setDecimalFormatSymbols(new DecimalFormatSymbols(Locale.US));
value = decimalFormat.format(numericValue);

これは、バイナリ形式で正確に表すことができない値に対しても機能します。

4

8 に答える 8

6

目標が小数点の右側に正確にn個の有効数字を含む数値を表すことである場合、 BigDecimalが使用するクラスです。

不変の任意精度の符号付き10進数。BigDecimalは、任意精度の整数のスケーリングされていない値と32ビットの整数スケールで構成されます。ゼロまたは正の場合、スケールは小数点の右側の桁数です。負の場合、数値のスケーリングされていない値に10を掛けて、スケールの否定の累乗にします。したがって、BigDecimalで表される数値の値は(unscaledValue×10scale)です。

scalesetScale(int)を介して設定できます

于 2008-11-05T12:15:21.737 に答える
6

2 進浮動小数点表現の精度 ( IEEE754 倍精度で約 16 桁) に達したため、テストは失敗します。649632196443.4279 を 10000 で乗算すると、バイナリ表現が切り捨てられ、後で丸めたり除算したりするときにエラーが発生し、関数の結果が完全に無効になります。

詳細については、http://en.wikipedia.org/wiki/Floating_point#Accuracy_problemsを参照してください。

n+1より良い方法は、小数点以下の桁数が特定のしきい値を下回っているかどうかを確認することです。d - round(d)より小さい場合( epsilonlimit を参照)、 の 10 進数表現にdは小数点以下の有効桁数がありません。同様に、(d - round(d)) * 10^nが 未満の場合、d は最大でも重要な場所epsilonを持てます。n

Jon Skeetを使用して、探している小数点以下の桁数を保持するのに十分な精度がない場合DoubleConverterを確認します。d

于 2008-11-05T13:07:09.713 に答える
3

すべての浮動小数点演算と同様に、等しいかどうかをチェックするのではなく、エラー(イプシロン)が十分に小さいことを確認する必要があります。

交換する場合:

return (d==check);

のようなもので

return (Math.abs(d-check) <= 0.0000001);

動作するはずです。明らかに、イプシロンは、チェックしている小数点以下の桁数と比較して十分に小さいものを選択する必要があります。

于 2008-11-05T12:04:26.860 に答える
1

タイプは2double進浮動小数点数です。それらを10進数の浮動小数点数であるかのように扱うことには、常に明らかな不正確さがあります。思い通りに機能するように関数を書くことができるかどうかはわかりません。

数値の元のソース(おそらく文字列入力)に戻って、重要な場合は10進表現を保持する必要があります。

于 2008-11-05T12:06:05.670 に答える
1

BigDecimalに切り替えることができる場合は、Ken Gが説明しているように、それを使用する必要があります。

そうでない場合は、他の回答で述べられているように、多くの問題に対処する必要があります。私にとって、あなたは2進数(double)を扱っており、その数値の10進表現について質問しています。つまり、文字列について質問しています。あなたの直感は正しいと思います。

于 2008-11-05T21:12:27.737 に答える
0

私はこれがより良いと思います文字列に変換し、指数の値を調べます

 public int calcBase10Exponet (Number increment)
 {
  //toSting of 0.0=0.0
  //toSting of 1.0=1.0
  //toSting of 10.0=10.0
  //toSting of 100.0=100.0
  //toSting of 1000.0=1000.0
  //toSting of 10000.0=10000.0
  //toSting of 100000.0=100000.0
  //toSting of 1000000.0=1000000.0
  //toSting of 1.0E7=1.0E7
  //toSting of 1.0E8=1.0E8
  //toSting of 1.0E9=1.0E9
  //toSting of 1.0E10=1.0E10
  //toSting of 1.0E11=1.0E11
  //toSting of 0.1=0.1
  //toSting of 0.01=0.01
  //toSting of 0.0010=0.0010  <== need to trim off this extra zero
  //toSting of 1.0E-4=1.0E-4
  //toSting of 1.0E-5=1.0E-5
  //toSting of 1.0E-6=1.0E-6
  //toSting of 1.0E-7=1.0E-7
  //toSting of 1.0E-8=1.0E-8
  //toSting of 1.0E-9=1.0E-9
  //toSting of 1.0E-10=1.0E-10
  //toSting of 1.0E-11=1.0E-11
  double dbl = increment.doubleValue ();
  String str = Double.toString (dbl);
//  System.out.println ("NumberBoxDefaultPatternCalculator: toSting of " + dbl + "=" + str);
  if (str.contains ("E"))
  {
   return Integer.parseInt (str.substring (str.indexOf ("E") + 1));
  }
  if (str.endsWith (".0"))
  {
   return str.length () - 3;
  }
  while (str.endsWith ("0"))
  {
   str = str.substring (0, str.length () - 1);
  }
  return - (str.length () - str.indexOf (".") - 1);
 }
于 2011-01-07T13:07:36.607 に答える
0

これが一般的に本当に実行可能かどうかはわかりません。たとえば、小数点以下の桁数は1.0e-13いくつですか。算術演算中の丸め誤差が原因で、実際には0偽装されている場合はどうなるでしょうか? 一方、オンの場合、最初のn桁にゼロ以外の数字があるかどうかを尋ねているので、次のようにすることができます。

   static boolean checkDecimalPlaces(double d, unsigned int decimalPlaces){
      // take advantage of truncation, may need to use BigInt here
      // depending on your range
      double d_abs = Math.abs(d);
      unsigned long d_i = d_abs; 
      unsigned long e = (d_abs - d_i) * Math.pow(10, decimalPlaces);
      return e > 0;
   }
于 2008-11-05T12:31:49.377 に答える