2016-12-07 28 views
2

bigdecimalを別のbigdecimalで除算し、除数の逆数をとり、被除数を掛けることで検証しようとしています。例えばスカラを使った逆数BigDecimal乗算と除算

、基本的な数学のことを私たちに教えてくれます:具体的には、ここで私たちがやろうとしているものであるa/b = a * (1/b)

6.0075/0.89 = 6.0075 * (1/0.89) = 6.75

追加精度のためのBigDecimalを使用して、我々は最初の部分を取得することができます

scala> BigDecimal(6.0075)/BigDecimal(0.89) 
res1: scala.math.BigDecimal = 6.75 

でもない二:

scala> BigDecimal(6.0075) * (BigDecimal(1.00)/BigDecimal(0.89)) 
res2: scala.math.BigDecimal = 6.749999999999999999999999999999999 

BigDecimal(1.00)/BigDecimal(0.89)は不正確な結果であり、Javaのmath.BigDecimalに関するScalaのBigDecimalラッパーは、RoundingModeがHALF_EVENの35小数点以下の精度を指定していることがわかります。残念ながら、1.00/0.89には無限の小数点以下桁数があるので、JavaのBigDecimalを直接使用するとどちらも役に立ちません。

他に誰かがこの問題を抱えていますか?式1.00/0.89を書き直そうとするのを除いて、ScalaまたはJavaのどちらにもこれを処理する方法はありますか?

ありがとうございます!

答えて

3

を全く事実を歩き回るが無限回帰の有限表現がされることはありません丸め誤差があります。 (Is floating point math broken?を参照)

私は2つの可能な解決方法を見ていますが、どちらも些細なものではありません。

  1. これらの値を表すには、Rational Numbersクラスを使用します。すべての値は分子全体と分母の両方を持っています。 GCDを追跡する必要がありますが、実際に2つの数字を分割する必要があるのは、画面(またはページ)に値を送るときだけです。
  2. 丸め誤差の各値を確認してください(値のStringのRegExパターンマッチングを使用するのが最適でしょう)。
-1

あなたはa/ba * (1/b)

問題と同じであることを確認したい:(1/b)の値は正確ではないかもしれません。

私の回避策:

はその後 a/bであることを確認します(あなたはそれが35小数点以下の桁に働くと言う) u = 0.00000000000000000000000000000000001をしてみましょう、少なくとも a * ((1/b) - u)と最大で a * ((1/b) + u)
aが負の場合、あなたがサインを交換する必要があります。その比較の これは完璧な回避策ではありませんが、それが実現することを願っています。

2

残念ながら、1.00/0.89(またはその場合は1/3)のような無限小数点の拡張で数値を表現する方法は100%正確ではありません。浮動小数点数を使用します。これは、無限のプロパティをエミュレートしようとする有限のマシンの制限です。

しかし(あなたが通貨を使用している場合のように2)あなたは数字だけの一定数の精度を気にしている場合、あなたはJavaのMathContextsetScale方法使用することができます:私は使用しています

scala> import java.math.{RoundingMode => RM, MathContext => MC} 
import java.math.{RoundingMode=>RM, MathContext=>MC} 

scala> val mc = new MC(100, RM.HALF_UP) 
mc: java.math.MathContext = precision=100 roundingMode=HALF_UP 

scala> val a = BigDecimal("6.0075") 
a: scala.math.BigDecimal = 6.0075 

scala> val b = BigDecimal("1") 
b: scala.math.BigDecimal = 1 

scala> val c = BigDecimal("0.89") 
c: scala.math.BigDecimal = 0.89 

scala> val d = (a * (b(mc)/c)).setScale(2) 
d: scala.math.BigDecimal = 6.75 

注意を(たとえ0.89も完全に浮動小数点数で表現することはできないので)より正確な表現であるため、double型のdouble-stringバージョンです。

EDIT

有理数について@ jwvhさんのコメントに触発さ

私は一緒にこの基本的なRationalクラスを投げた:

scala> :pa 
// Entering paste mode (ctrl-D to finish) 

object Rational 
{ 
    def apply(n: BigInt, d: BigInt): Rational = 
    { 
     val neg_mod = if (d < BigInt(0)) BigInt(-1) else BigInt(1) 
     val (n_mod, d_mod) = (neg_mod * n, neg_mod * d) 
     val gcd_val = gcd(n_mod, d_mod) 
     new Rational(n_mod/gcd_val, d_mod/gcd_val) 
    } 
    def gcd(a: BigInt, b: BigInt): BigInt = if (b == BigInt(0)) a else gcd(b, a % b) 
} 
class Rational(val n: BigInt, val d: BigInt) 
{ 
    override def toString: String = if (n == BigInt(0)) "0" else if (d == BigInt(1)) s"$n" else s"$n/$d" 

    def toDouble: Double = n.toDouble/d.toDouble 

    def *(that: Rational): Rational = Rational(n * that.n, d * that.d) 

    def /(that: Rational): Rational = Rational(n * that.d, d * that.n) 

    def +(that: Rational): Rational = Rational(n * that.d + that.n * d, d * that.d) 

    def -(that: Rational): Rational = this + (-that) 

    def unary_- = Rational(-n, d) 
} 

// Exiting paste mode, now interpreting. 

defined object Rational 
defined class Rational 

scala> val a = Rational(60075, 10000) 
a: Rational = 2403/400 

scala> val b = Rational(1, 1) 
b: Rational = 1 

scala> val c = Rational(89, 100) 
c: Rational = 89/100 

scala> a * (b/c) 
res0: Rational = 27/4 

scala> (a * (b/c)).toDouble 
res1: Double = 6.75