2024 年 Clojure 状态调查! 中分享您的看法。

欢迎!请在 关于 页面查看更多关于该功能的信息。

0
Java 互操作

当任何参数为浮点数时,{{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 也会对 double 进行除零检查,这与 double 的除法行为不一致

(/ 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 text: this commit)返回原始 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 text: Clojure dev)。

7 个答案

0
_评论区:favila_

更多测试显示,{{n % d}} 并没有保存如下关系 {{(= n (+ (* d (quot n d)) (rem n d)))}} 以及 {{(n - d * (quot n d))}},这对我来说没有意义,因为这是[JLS|http://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.17.3] 中说的%所保持的关系。似乎%并不只是与不同舍入的Math.IEEEremainder()相关。

测试用例:(rem 40.0 0.1) == 0.0; 40.0 % 0.1 == 0.0999...(更小的分子仍然不会精确地落在0上,但会比%靠近。)

更新后的补丁,回滚了一些简化到余数的部分,并添加了这个测试用例。
0

评论区:jafingerhut

Francis,您2015年3月24日发布的补丁clj-1680_no_div0.patch使用isFinite()方法,这种方法似乎是在Java 1.8中添加的,在早期版本中不存在。我猜,当Clojure的下个版本可能放弃对Java 1.6的支持时,同时放弃对Java 1.7的支持的可能性较小。如果您的补丁能使用类似于!(isInfinite() | isNaN())的某种方法,我认为它是等效的,并且这两种方法都存在于早期的Java版本中。

0

评论区:favila

更新后的补丁带有1.7版本,同时也基于master分支。

除了这个之外,没有其他测试失败。

`

 [java] FAIL in (gen-interface-source-file) (genclass.clj:151)
 [java] expected: (= "examples.clj" (str sourceFile))
 [java]   actual: (not (= "examples.clj" ""))

`

0

评论区:michaelblume

Francis,我尝试下载您的补丁,我没有看到任何测试失败。如果您从Clojure仓库检出master分支,您是否会看到同样的失败?如果您运行mvn clean后是否仍然看到?如果是这样,这可能值得为此打开一个工单,看看是否有人能重现它。

0

评论区:jafingerhut

是的,如果您在使用未经修改的Clojure执行'mvn clean test'时看到失败,或者使用 './antsetup.sh ; ant clean; ant' 时出现失败,请告诉我们您所使用的操作系统和JVM。我在尝试过的操作系统/JVM组合中都没有见过这种情况。

0

评论区:favila

没关系,在清洁后失败测试已经消失了。所有测试都通过了。

0
参考: https://clojure.atlassian.net/browse/CLJ-1680(由favila报告)
...