{{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 的实现,有时(当结果除法大于一个长值时)会先取一个 double,将其强制转换为 BigDecimal,然后转换为 BigInteger,最后又转回 double。这种强制转换意味着它无法处理中间的非有限值。这一点都是完全不必要的,我认为这仅仅是当这些方法曾经返回一个包装了整数类型(long 或 BigInteger)时的遗留问题。改动了(链接: https://github.com/clojure/clojure/commit/e4a76e058ceed9b152ffd00b3f83e2800207bc25 文本:这次改动的提交)使其返回原始 double,但方法体没有被彻底重构。
方法体应当简单地是
`
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)。