请在 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

Francis,我尝试下载您的补丁,我没有看到任何测试失败。您从Clojure仓库的master分支检出时是否看到相同的失败?如果先运行mvn clean,是否仍然看到它?如果是这样,打开一个ticket并看看是否其他人也能重现它可能是个不错的决定。

0 投票

评论由:jafingerhut

是的,如果你在使用未经修改的Clojure进行'mvn clean test'或'./antsetup.sh ; ant clean; ant'时发现失败,请告诉我们你使用的操作系统和JVM。我还没有在我的实验中看到这些组合上的问题。

0 投票

评论由:favila

没关系,清理之后失败的测试消失了。所有测试都通过了。

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