请在 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 也对双精度浮点数进行除以零检查,这与双精度浮点数的除法行为不一致

(/ 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)。

7 个回答

0
by
_评论人:favila_

更多测试显示,{{n % d}} 没有保留关系 {{(= n (+ (* d (quot n d)) (rem n d)))}} 和 {{(n - d * (quot n d))}},我觉得这没有意义,因为这是 [规范|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
by

评论人:jafingerhut

Francis,您的补丁clj-1680_no_div0.patch,日期为2015年3月24日,使用了 isFinite() 方法,这似乎是在 Java 1.8 中添加的,在早期版本中不存在。我猜测,虽然 Clojure 的下一个版本可能放弃对 Java 1.6 的支持,但它不太可能同时放弃对 Java 1.7 的支持。如果您的补丁可以使用类似 !(isInfinite() | isNaN()) 的东西,这可能更合适,我相信它是等效的,并且这两个方法都存在于早期的 Java 版本中。

0
by

评论人:favila

更新了补丁,具有与 Java 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
by

评论人: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](https://clojure.atlassian.net/browse/CLJ-1680)(由favila汇报)
...