2

任意精度が必要な場合は、Java浮動小数点プリミティブ値を使用しないことが知られています。ゲッツは彼の優れた記事で問題を説明しました。

特定のプロジェクトで任意精度を達成する必要があり、BigDecimalクラスがなく(APIで使用できないため:JavaMEなど)、カスタム実装を開発する時間がない場合を想像してみてください。必要な精度が比較的小さい(小数点以下2〜4)ことを事前に知っていれば、フロート型とダブル型、および丸め関数を使用して、100%信頼性の高い緊急回避策を実装できますか?もしそうなら、APIのどの関数を使用できますか?この機能が利用できなかったが、それでも問題に対処できると思う場合、それを実装するのはどれほど複雑でしょうか?

4

5 に答える 5

3

いいえ、浮動小数点演算を使用して表現できない値があるため、それは不可能です。0.1最も簡単な例です。

于 2011-12-14T15:09:36.563 に答える
1

「100%信頼できる」と定義します。IEEE 754浮動小数点値(ほぼすべての言語で使用されます。これはJava固有の問題ではありません)は、実際には、非常に信頼性の高い方法で設計された処理を実行します。それらは、人々が(10進数の)小数の動作を期待するように常に動作するとは限りません。

浮動小数点数に関する問題を解決するものが必要な場合は、最初に、問題が何であるか、およびこれらのインスタンスでこの新しい形式がどのように動作するかを正確に指定する必要があります。

于 2011-12-14T15:13:15.880 に答える
1

いいえ。

0.15の半分は、100分の1に四捨五入されていますか?

正確な算術では、0.15 / 2 = 0.075であり、 0.08に切り上げられます(切り上げまたは切り上げのいずれかのルールを想定)。

IEEE 754演算では、0.15 / 2 = 0.07499999999999999722444243843710864894092082977294921875であり、0.07に切り捨てられます。

于 2011-12-16T00:02:41.907 に答える
0

この場合、なぜ浮動小数点演算に煩わされるのでしょうか。Integer精度係数を掛けたものを使用してください。

final int PRECISION = 4;
Integer yourFloatingValue = Integer.valueOf("467.8142") * Math.pow(10, PRECISION);

などの小さな精度の値は、標準の演算467.8142で表され、使用して計算されます。精度を損なうことはありません。4,678,142Integer

しかし、繰り返しになりますが、@ TomaszNurkiewiczが述べたように、これはまさにその通りBigDecimalです。したがって、あなたの質問は実際には意味がありません。浮動小数点演算は完全に問題なく、プログラマーが自分のしていることを知っていれば、あなたが言及したケースでも処理できます。

于 2011-12-14T15:05:54.910 に答える
0

すべての丸めを除外する程度にすべての数学を完全に定義および制御できない限り、私はノーだと思います。

別の方法として、おそらくRationalsを使用することもできます。これは私が実験としてノックアップしたものです。それが最適であるか、それとも効率的であるかは疑問ですが、それは確かに可能性です。

class Rational {

  private int n; // Numerator.
  private int d; // Denominator.

  Rational(int n, int d) {
    int gcd = gcd(n, d);
    this.n = n / gcd;
    this.d = d / gcd;
  }

  Rational add(Rational r) {
    int lcm = lcm(d, r.d);
    return new Rational((n * lcm) / d + (r.n * lcm) / r.d, lcm);
  }

  Rational sub(Rational r) {
    int lcm = lcm(d, r.d);
    return new Rational((n * lcm) / d - (r.n * lcm) / r.d, lcm);
  }

  Rational mul(Rational r) {
    return new Rational(n * r.n, d * r.d);
  }

  Rational div(Rational r) {
    return new Rational(n * r.d, d * r.n);
  }

  @Override
  public String toString() {
    return n + "/" + d;
  }

  /**
   * Returns the least common multiple between two integer values.
   * 
   * @param a the first integer value.
   * @param b the second integer value.
   * @return the least common multiple between a and b.
   * @throws ArithmeticException if the lcm is too large to store as an int
   * @since 1.1
   */
  public static int lcm(int a, int b) {
    return Math.abs(mulAndCheck(a / gcd(a, b), b));
  }

  /**
   * Multiply two integers, checking for overflow.
   * 
   * @param x a factor
   * @param y a factor
   * @return the product <code>x*y</code>
   * @throws ArithmeticException if the result can not be represented as an
   *         int
   * @since 1.1
   */
  public static int mulAndCheck(int x, int y) {
    long m = ((long) x) * ((long) y);
    if (m < Integer.MIN_VALUE || m > Integer.MAX_VALUE) {
      throw new ArithmeticException("overflow: mul");
    }
    return (int) m;
  }

  /**
   * <p>
   * Gets the greatest common divisor of the absolute value of two numbers,
   * using the "binary gcd" method which avoids division and modulo
   * operations. See Knuth 4.5.2 algorithm B. This algorithm is due to Josef
   * Stein (1961).
   * </p>
   * 
   * @param u a non-zero number
   * @param v a non-zero number
   * @return the greatest common divisor, never zero
   * @since 1.1
   */
  public static int gcd(int u, int v) {
    if (u * v == 0) {
      return (Math.abs(u) + Math.abs(v));
    }
    // keep u and v negative, as negative integers range down to
    // -2^31, while positive numbers can only be as large as 2^31-1
    // (i.e. we can't necessarily negate a negative number without
    // overflow)
      /* assert u!=0 && v!=0; */
    if (u > 0) {
      u = -u;
    } // make u negative
    if (v > 0) {
      v = -v;
    } // make v negative
    // B1. [Find power of 2]
    int k = 0;
    while ((u & 1) == 0 && (v & 1) == 0 && k < 31) { // while u and v are
      // both even...
      u /= 2;
      v /= 2;
      k++; // cast out twos.
    }
    if (k == 31) {
      throw new ArithmeticException("overflow: gcd is 2^31");
    }
    // B2. Initialize: u and v have been divided by 2^k and at least
    // one is odd.
    int t = ((u & 1) == 1) ? v : -(u / 2)/* B3 */;
    // t negative: u was odd, v may be even (t replaces v)
    // t positive: u was even, v is odd (t replaces u)
    do {
      /* assert u<0 && v<0; */
      // B4/B3: cast out twos from t.
      while ((t & 1) == 0) { // while t is even..
        t /= 2; // cast out twos
      }
      // B5 [reset max(u,v)]
      if (t > 0) {
        u = -t;
      } else {
        v = t;
      }
      // B6/B3. at this point both u and v should be odd.
      t = (v - u) / 2;
      // |u| larger: t positive (replace u)
      // |v| larger: t negative (replace v)
    } while (t != 0);
    return -u * (1 << k); // gcd is u*2^k
  }

  static void test() {
    Rational r13 = new Rational(1, 3);
    Rational r29 = new Rational(2, 9);
    Rational r39 = new Rational(3, 9);
    Rational r12 = new Rational(1, 2);
    Rational r59 = r13.add(r29);
    Rational r19 = r29.mul(r12);
    Rational r23 = r39.div(r12);
    Rational r16 = r12.sub(r13);
    System.out.println("1/3 = " + r13);
    System.out.println("2/9 = " + r29);
    System.out.println("1/3 = " + r39);
    System.out.println("5/9 = " + r59);
    System.out.println("1/9 = " + r19);
    System.out.println("2/3 = " + r23);
    System.out.println("1/6 = " + r16);
  }
}

java2でlcmとgcdのコードを見つけました。それらはおそらく改善することができます。

于 2011-12-14T16:16:46.507 に答える