{{quot}}和{{rem}}在双精度浮点数的情况下(其中任意一个参数是浮点数)对于非有限参数产生奇怪的结果
(quot Double/POSITIVE_INFINITY 2) ; Java: Infinity NumberFormatException Infinite or NaN java.math.BigDecimal.<init> (BigDecimal.java:808) (quot 0 Double/NaN) ; Java: NaN NumberFormatException Infinite or NaN java.math.BigDecimal.<init> (BigDecimal.java:808) (quot Double/POSITIVE_INFINITY Double/POSITIVE_INFINITY) ; Java: NaN NumberFormatException Infinite or NaN java.math.BigDecimal.<init> (BigDecimal.java:808) (rem Double/POSITIVE_INFINITY 2) ; Java: NaN NumberFormatException Infinite or NaN java.math.BigDecimal.<init> (BigDecimal.java:808) (rem 0 Double/NaN) ; Java: NaN NumberFormatException Infinite or NaN java.math.BigDecimal.<init> (BigDecimal.java:808) (rem 1 Double/POSITIVE_INFINITY) ; 最奇怪的一个。Java: 1.0 => NaN
quot和rem也为双精度浮点数执行除以零检查,这与双精度浮点数的除法行为不一致
(/ 1.0 0) => NaN (quot 1.0 0) ; Java: NaN ArithmeticException Divide by zero clojure.lang.Numbers.quotient (Numbers.java:176) (rem 1.0 0); Java: NaN ArithmeticException Divide by zero clojure.lang.Numbers.remainder (Numbers.java:191)
附加的补丁(强烈不推荐)没有解决这个问题,因为我不确定这是否是期望的行为。上述提及的行为没有经过测试。
这种行为的基本原因是,quot和rem的实现在某些情况下(当结果大于long时)会取一个double,将其强制转换为BigDecimal,然后转换为BigInteger,然后再转回double。这种转换意味着它不能处理非有限中间值。所有这些都是完全没有必要的,我认为这只是当这些方法使用返回包装整数类型(long或BigInteger)时遗留的废品。这发生在(链接: https://github.com/clojure/clojure/commit/e4a76e058ceed9b152ffd00b3f83e2800207bc25 文本:此提交)改回返回原始双精度浮点数,但这些方法体没有经过足够的重构。
方法体应该简单地是
`
static public double quotient(double n, double d){
if(d == 0)
throw new ArithmeticException("Divide by zero");
double q = n / d;
return (q >= 0) ? Math.floor(q) : Math.ceil(q);
}
static public double remainder(double n, double d){
if(d == 0)
throw new ArithmeticException("Divide by zero");
return n % d;
}
`
这也是附加的补丁所做到的。(而且我甚至不确定{{d==0}}检查是否合适。)
即使爆炸式非有限结果对于quot和rem是有意义的属性,也没有必要进行BigDecimal+BigInteger转换。我可以准备一个补丁来保留现有行为但更加高效。
更多讨论在(链接: https://groups.google.com/d/msg/clojure-dev/nSqIfpqSpRM/kp3f5h-zONYJ 文本:Clojure dev)。