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 的实现有时候(当除法结果大于 long 类型时)会将 double 类型的值强制转换为 BigDecimal,然后转换为 BigInteger,最后再转换回 double。这种强制转换意味着它不能处理非有限中间值。所有这些都是完全没有必要的,我认为这只是当这些方法曾经返回一个boxed的整数类型(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 开发者群组)。

7 答案

0 投票
_由 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 投票

由 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 投票

由 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 投票

由 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报告)
...