Unfortunately there is no 100% accurate way to represent numbers with an infinite decimal expansion like 1.00/0.89
(or 1/3
for that matter) with floating point numbers. This is a limitation of finite machines trying to emulate infinite properties.
However if you only care about accuracy to a certain number of digits (like 2 if you are using currency) then you could use Java's MathContext
and the setScale
method:
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
Note that I am using the string version of your doubles because they are a more precise representation (because even 0.89
cannot be represented in floating point numbers perfectly).
EDIT:
Inspired by @jwvh's comment about Rational Numbers I threw this basic Rational
class together:
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