Clojure 2024年度调查中分享您的看法!

欢迎!请参阅关于页面了解如何使用本网站的相关信息。

+1
Clojure

这与问题1555有关,但涉及的数据类型不同,因此潜在问题可能也不同。

`rem`在bigint和float之间似乎返回极其不正确的结果。请注意使用`10000`作为除数与使用`1e4`之间的巨大差异

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

by
这是一个公正的回答,也许还是对核心团队回应的正确推测!生成测试只会让你直面这些问题 :) 这本身就是一个很好的教训,也是使用test.check的理由。
...