How do I subtract 0.1 and keep the precision to 1?

I’d like to get this series of numbers:

10.0

9.9

9.8

9.7

9.6

and so on

But if I do this:

(loop [meat-per-hectare (java.math.BigDecimal. 10.0)
total (java.math.BigDecimal. 0.0)]
(print “\n\n”)
(print meat-per-hectare)
(if (< meat-per-hectare 1)
total
(recur (with-precision 1 (- meat-per-hectare 0.1))
(with-precision 1 (+ meat-per-hectare total)))))

I get:

10M

9.9

9.8

9.700000000000001

9.600000000000001

9.500000000000002

So it looks like the numbers start as I want them, but then get cast to doubles. Since “with precision” only works on BigDecimal, it doesn’t work in the (recur) section, where I assume the numbers have been automatically cast to double?

However, if I try this:

(loop [meat-per-hectare (java.math.BigDecimal. 10.0)
total (java.math.BigDecimal. 0.0)]
(print “\n\n”)
(print meat-per-hectare)
(if (< meat-per-hectare 1)
total
(recur (with-precision 1 (java.math.BigDecimal. (- meat-per-hectare 0.1)))
(with-precision 1 (java.math.BigDecimal. (+ meat-per-hectare total))))))

I get:

No matching ctor found for class java.math.BigDecimal

I assume the cast to BigDecimal is expecting an Integer for input, not a double.

So what is the correct way to do this?

I’m also surprised by these numbers:

tlaoa.core=> (java.math.BigDecimal. 0.1)

0.1000000000000000055511151231257827021181583404541015625M

tlaoa.core=> (with-precision 1 (java.math.BigDecimal. 0.1))

0.1000000000000000055511151231257827021181583404541015625M

(with-precision 1 ...) removes the fraction part completely on arithmetic operations on instances of BigDecimal, so you probably want (with-precision 2 ...).

The behavior that you see is due to the fact that an arithmetic operation on an Object and a double calls doubleValue on that Object after casting it to Number. In other words, explicit doubles win.
To circumvent that, convert your delta to BigDecimal as well:

(let [init (java.math.BigDecimal. 2.0)
      delta (java.math.BigDecimal. 0.1)]
  (into []
        (take-while #(>= % 1))
        (iterate #(with-precision 2 (- % delta)) init)))
=> [2M 1.9M 1.8M 1.7M 1.6M 1.5M 1.4M 1.3M 1.2M 1.1M 1.0M]
2 Likes

Thank you very much.