squants icon indicating copy to clipboard operation
squants copied to clipboard

Loss of precision when dividing Quantity

Open camjo opened this issue 8 years ago • 2 comments

When you deal with money, there is a method

def /(that: BigDecimal): Money = new Money(amount / that)(currency)

that allows you to divide money by an arbitrarily large BigDecimal without losing precision.

There is no such method on regular Quantity types:

e.g.

Grams(5.1) / BigDecimal(1000)
// cannot be applied to (scala.math.BigDecimal)
//       Grams(5.1) / BigDecimal(1000)

As such it appears to be impossible to divide Quantity types without potentially losing precision.

Even using the divide method produces a loss of precision:

Grams(5.1).divide(1000)
// squants.mass.Mass = 0.0050999999999999995 g

divideAndRemainder appears to handle the precision correctly but is a tedious interface for a simple division:

Grams(5.1).divideAndRemainder(1000)
// (squants.mass.Mass, squants.mass.Mass) = (0.0 g,5.1 g)

The only way I can see to actually produce the correct result currently is with the following approach:

Grams(5.1).map(x => (x / BigDecimal(1000)).toDouble)
// squants.mass.Mass = 0.0051 g

Perhaps this method could be added to Quantity?

def /(that: BigDecimal): A = this.map(v => (v / that).toDecimal)

Happy to raise a pull request if this solution makes sense!

camjo avatar Jun 06 '17 08:06 camjo

Similar to #253 this is a known issue b/c the library currently uses doubles. For 2.x, this will be fixed.

derekmorr avatar Jun 06 '17 14:06 derekmorr

@camjo The map method is the best choice for overcoming these types of limitations. I do agree with @derekmorr that making the underlying value generic instead of Double will go a great way toward improving the precision of the library.

In the meantime, I do think your proposed method would be a worthy addition.

garyKeorkunian avatar Jun 15 '17 21:06 garyKeorkunian