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

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

+1
Clojure

这与问题1555相关,但问题中的类型不同,所以根本问题可能也不同。

当你使用`10000`作为除数而不是`1e4`时,`rem`似乎返回非常不正确的结果。注意这里的大差异。

user> (rem 9037601485536036300227801N 10000)
7801N
user> (long (rem 9037601485536036300227801N 1e4))
1073741824

test.check提供了一个更紧密的示例,清楚地显示了不正确的结果。

user> (rem 9007199254740993N 2)
1N
user> (rem 9007199254740993N 2.0)
0.0

这里有一个生成测试来再现这种结果

(clojure.test.check/quick-check
 100 (prop/for-all [l gen/size-bounded-bigint
                    r (gen/fmap inc gen/nat)]
                   (== (rem l (double r))
                       (rem l (long r)))))

1 个回答

0

被选中
 
最佳答案

我相信,当前Clojure/JVM对(rem 9007199254740993N 2.0)的实现与(rem (double 9007199254740993N) 2.0)相同,所以对于足够大的BigInt值,会在最低有效位进行某种四舍五入,因此对于小的除数,肯定会给出错误的数值答案。

处理任意水平和精度的任意数值参数似乎是一个艰巨的任务,在思考、开发、测试、修复未预料到的边缘案例等方面都是如此。

我在这里不是决策者,但如果Clojure核心开发团队将这种行为归类为“如果你想得到精确的结果,不要使用float/double,去读‘每个计算机科学家都应该知道的关于浮点算术的’”,我并不感到惊讶。

https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html

这是一个合理的回应,也是对核心团队回应的准确猜测!生成测试只是将这些内容推给你的脸 :) 这本身就是一个很好的教训,也是要在所有地方使用test.check的原因。
...