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。这种转换意味着它无法处理非有限中间值。所有这些都是完全不必要的,我认为这仅仅是当这些方法原本返回一个装箱的整型类型(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
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,你于2015年3月24日创建的补丁clj-1680_no_div0.patch使用了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

弗朗西斯,我尝试下载你的补丁,但没有看到任何测试失败。你是否在检查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报告)
...