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

欢迎!请查阅 关于 页面了解有关此如何工作的更多信息。

0 投票
Java Interop

quotrem 在双精度浮点数(其中一个参数为浮点数)的情况下,对于非有限参数给出奇怪的结果

(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 开发者)。

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发表的评论

是的,如果您在执行'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报告)
...