quot 和 rem 在双精度浮点数(其中一个参数为浮点数)的情况下,对于非有限参数给出奇怪的结果
(quot Double/POSITIVE_INFINITY 2);Java:Infinity NumberFormatException Infinite 或 NaN java.math.BigDecimal.<init> (BigDecimal.java:808);(quot 0 Double/NaN);Java:NaN NumberFormatException Infinite 或 NaN java.math.BigDecimal.<init> (BigDecimal.java:808);(quot Double/POSITIVE_INFINITY Double/POSITIVE_INFINITY);Java:NaN NumberFormatException Infinite 或 NaN java.math.BigDecimal.<init> (BigDecimal.java:808);(rem Double/POSITIVE_INFINITY 2);Java:NaN NumberFormatException Infinite 或 NaN java.math.BigDecimal.<init> (BigDecimal.java:808);(rem 0 Double/NaN);Java:NaN NumberFormatException Infinite 或 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 除零 clojure.lang.Numbers.quotient (Numbers.java:176);(rem 1.0 0);Java:NaN ArithmeticException 除零 clojure.lang.Numbers.remainder (Numbers.java:191)
附加的补丁 没有 解决此问题,因为我不知道这是否是预期行为。没有测试断言上述任何行为。
这种行为的基本原因是 quot 和 rem 的实现有时(当结果大于一个 long)会取一个 double,将其强制转换为 BigDecimal,然后是 BigInteger,最后再回到 double。这种转换意味着它无法处理非有限中间值。所有这些都是完全不必要的,我认为这只是当这些方法返回一个boxed integer type(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 开发者)。