is clojureclr the same languare as on the jvm?
Essentially, yes. There interop stuff is different -- obviously, since it's .NET interop on the CLR but Java interop on the JVM.
And you can write cross-platform code in .cljc files that run on the CLR, the JVM, and in the browser/on node.js.
The main difference right now is tooling, which is a lot more mature for JVM Clojure but that's being worked on so the CLR version will soon be fairly similar.
What's the recommended way to take the ceiling of a BigDecimal?
(BigDecimal/Rescale 0.09M 0 clojure.lang.BigDecimal+RoundingMode/Ceiling) => 0M
I think I have a fix. It's just a small change in Rescale. Details available upon request.
For starters, I always have trouble figuring out what is the correct behavior in Quantize, Rescale , and Round.
For example, if you have 0.06M and you Rescale with Ceiling to an exponent of 1, it seems that the answer should be 1E+1M. Looking carefully at the specification, the answer would be 0, but because we lost some digits, Ceiling says we must increment the coefficent by one. And the exponent is required to be 1. So there we are.
Here are my test cases run with the modified code. Preliminaries:
(import '[clojure.lang BigDecimal BigDecimal+RoundingMode])
(def mhu BigDecimal+RoundingMode/HalfUp)
(def mc BigDecimal+RoundingMode/Ceiling)
(def mf BigDecimal+RoundingMode/Floor)
;; a little helper -- given a BigDecimal and a rounding mode, do a rescale with several different exponents
(defn f [bd mode exps] (map #(BigDecimal/Rescale bd % mode) exps))
Doing some tests with half-up:
(f 0.4M mhu [-3 -2 -1 0 1]) ; => (0.400M 0.40M 0.4M 0M 0E+1M)
(f 0.6M mhu [-3 -2 -1 0 1]) ; => (0.600M 0.60M 0.6M 1M 0E+1M)
(f 0.04M mhu [-3 -2 -1 0 1]) ; => (0.040M 0.04M 0.0M 0M 0E+1M)
(f 0.06M mhu [-3 -2 -1 0 1]) ; => (0.060M 0.06M 0.1M 0M 0E+1M)
Those look okay, I think. We see rounding up in the appropriate places. Let's try with Ceiling:
(f 0.4M mc [-3 -2 -1 0 1]) ; => (0.400M 0.40M 0.4M 1M 1E+1M)
(f 0.6M mc [-3 -2 -1 0 1]) ; => (0.600M 0.60M 0.6M 1M 1E+1M)
(f 0.04M mc [-3 -2 -1 0 1]) ; => (0.040M 0.04M 0.1M 1M 1E+1M)
(f 0.06M mc [-3 -2 -1 0 1]) ; => (0.060M 0.06M 0.1M 1M 1E+1M)
the 0 exponent cases in the last two lines were the ones giving us a problem in our tests of the old code earlier in this thread.
You suggested two more test cases:
(BigDecimal/Rescale -0.1M 0 mc) ; => -1M
(BigDecimal/Rescale -0.1M 0 mf) ; => 0M
And my test suite is running green. (I have about 4000 tests on BigDecimal. Just not many on Ceiling.)
I'll have this up in the next alpha release.
If you want to compile from source before then, I can tell you edit to make. (I probably won't have a commit to the repo for another day. I have to do some other cleanup first.)Modified BigDecimal code now in repo.
Nice! Quite a subtle fix. Thanks!
Apparently I should have trusted the magic of BigDecimal.Round.
Sounds good. Here's another ceiling test case:
(BigDecimal/Rescale -0.1M 0 clojure.lang.BigDecimal+RoundingMode/Ceiling) ; => -1M
And floor:
(BigDecimal/Rescale -0.1M 0 clojure.lang.BigDecimal+RoundingMode/Floor) ; => 0MMy fourth guess would have been what you wrote.
My first three guesses would have been wrong.
It's been a long time since I worked on this code. I've forgotten most of it.
Not just the code. Despite having implemented it, I remained mystified by some operations.
BigDecimal.Rescale certainly falls in that category.
There are tests in the test suite that deal with Rescale -- over 400, actually. However, they all have rounding mode = HalfUp. It is possible there is a problem with Ceiling.
I'll do some digging.
I'm not sure what the exact problem is. It's something to do with precision vs scale. A quick experiment:
; define modes to use
(def mc (clojure.lang.BigDecimal+RoundingMode/Ceiling))
(def mhu (clojure.lang.BigDecimal+RoundingMode/HalfUp))
; check out something that should work just fine
(BigDecimal/Rescale 123.45M 0 mhu) ; => 123M
(BigDecimal/Rescale 123.55M 0 mhu) ; => 124M
(BigDecimal/Rescale 123.45M 0 mc) ; => 124M
(BigDecimal/Rescale 123.55M 0 mc) ; => 124M
; check out values less than 1 -- this first set looks okay
(BigDecimal/Rescale 0.45M 0 mhu) ; => 0M
(BigDecimal/Rescale 0.55M 0 mhu) ; => 1M
(BigDecimal/Rescale 0.45M 0 mc) ; => 1M
(BigDecimal/Rescale 0.55M 0 mc) ; => 1M
; let's drop our precision -- still okay
(BigDecimal/Rescale 0.4M 0 mhu) ; => 0M
(BigDecimal/Rescale 0.6M 0 mhu) ; => 1M
(BigDecimal/Rescale 0.4M 0 mc) ; => 1M
(BigDecimal/Rescale 0.6M 0 mc) ; => 1M
; drop down a decimal place, and suddenly we are in trouble
(BigDecimal/Rescale 0.04M 0 mhu) ; => 0M
(BigDecimal/Rescale 0.06M 0 mhu) ; => 0M
(BigDecimal/Rescale 0.04M 0 mc) ; => 0M ** BAD
(BigDecimal/Rescale 0.06M 0 mc) ; => 0M ** BAD
I'm really going to have to dig into the spec to figure this one out.
Y'all are welcome to join in: https://speleotrove.com/decimal/decarith.pdf
Could be a problem in Rescale, though it works for all my test cases -- until now!