Why do quot and rem cast through BigInteger?

137 views
Skip to first unread message

Francis Avila

unread,
Mar 23, 2015, 4:49:14 PM3/23/15
to cloju...@googlegroups.com
This is the implementation of quot and rem for doubles:

static public double quotient(double n, double d){
 
if(d == 0)
 
throw new ArithmeticException("Divide by zero");


 
double q = n / d;
 
if(q <= Long.MAX_VALUE && q >= Long.MIN_VALUE)
 
{
 
return (double)(long) q;
 
}
 
else
 
{ //bigint quotient
 
return new BigDecimal(q).toBigInteger().doubleValue();
 
}
}


static public double remainder(double n, double d){
 
if(d == 0)
 
throw new ArithmeticException("Divide by zero");


 
double q = n / d;
 
if(q <= Long.MAX_VALUE && q >= Long.MIN_VALUE)
 
{
 
return (n - ((long) q) * d);
 
}
 
else
 
{ //bigint quotient
 
Number bq = new BigDecimal(q).toBigInteger();
 
return (n - bq.doubleValue() * d);
 
}
}


In both cases, results of primitive division with large magnitude (greater than a long) are cast to bigdecimal, then truncated by casting to biginteger, then cast back to double. Why is this done instead of just truncating the double directly (e.g. with Math/floor or Math/ceil)? Ideas I had:

  1. Some subtlety of double rounding. Maybe if the quotient's significand exceeds 53 bits, somehow you can get a better approximation? This doesn't seem possible, though, since the bits are already gone at q, and if there were regained by bq they are gone again when cast to double.
  2. Enforce with-precision settings for rounding. But this isn't done for smaller magnitude, so anyone relying on this behavior will not get expected results anyway.
  3. Guarantee that a q of +/- Infinity or NaN will throw. Why not just check directly?


Francis Avila

unread,
Mar 23, 2015, 6:00:18 PM3/23/15
to cloju...@googlegroups.com
I did some more experimenting looking for doubles which would return a different double when truncated via BigInteger vs Math/floor or Math/ceil. I couldn't think of good values so I just take random ones (test-check approach). I haven't found any yet, but I've only run tens-of-millions of trials. Code below.


(defn bdtrunc ^double [^double x] (.. (BigDecimal. x) (toBigInteger) (doubleValue)))


(defn dbltrunc ^double [^double x] (if (>= x 0.0) (Math/floor x) (Math/ceil x)))


(defn rand-range [lower upper]
 
(let [factor (Math/random)]
   
(long (Math/floor (+ lower (- (* factor (+ 1.0 upper))
                                 
(* factor lower)))))))


(defn rand-double []
 
(let [sig (rand-range -4503599627370496 4503599627370496)
        exp
(rand-range -1022 1023)]
   
(Math/scalb (double sig) exp)))


(defn find-trunc-diffs [trials]
 
(for [n (repeatedly trials rand-double)
       
:when (not (or (Double/isNaN n) (Double/isInfinite n)))
       
:let [bd (bdtrunc n)
              dbl
(dbltrunc n)]
       
:when (not= bd dbl)]
   
[bd dbl]))


(defn print-raw-double [^double x]
 
(println (format "%64s" (Long/toBinaryString (Double/doubleToRawLongBits x)))))

Alex Miller

unread,
Mar 23, 2015, 6:08:35 PM3/23/15
to cloju...@googlegroups.com

--
You received this message because you are subscribed to the Google Groups "Clojure Dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure-dev...@googlegroups.com.
To post to this group, send email to cloju...@googlegroups.com.
Visit this group at http://groups.google.com/group/clojure-dev.
For more options, visit https://groups.google.com/d/optout.

Francis Avila

unread,
Mar 23, 2015, 6:20:28 PM3/23/15
to cloju...@googlegroups.com
These deal with longs and bigints, not doubles, and the patches don't seem to touch any code paths that would reach the quotient and remainder methods for doubles. I'll investigate a bit more later, but I don't understand how these are relevant. If you could connect the dots a bit more I'd appreciate it.

Alex Miller

unread,
Mar 23, 2015, 9:56:59 PM3/23/15
to cloju...@googlegroups.com
Ah, sorry I was just skimming and saw biginteger in there and I knew those were some outstanding tickets.  Disregard! 

Francis Avila

unread,
Mar 24, 2015, 12:10:37 AM3/24/15
to cloju...@googlegroups.com
Looking at the commit history of these lines I think now that this is just detritus left over from before the unboxed math refactors. These methods used to return boxed numbers. I will create a ticket and patch when I get a chance.

Francis Avila

unread,
Mar 24, 2015, 11:58:39 AM3/24/15
to cloju...@googlegroups.com
Reply all
Reply to author
Forward
0 new messages