在 Clojure 或 Java 中,如何有效地检查一个数字在舍入到两位小数后是否等于整数?
In Clojure or Java, how can I efficiently check whether a number is equivalent to an integer after rounding to two fractional digits?
我基本上需要 Clojure 中的一个函数,例如 rounds-to-int?
,它的作用相当于:
- 取任意数(整数、浮点数或双精度数)
- 最多保留两位小数(即
1.000001234 => 1.00
)
- 判断小数点后是否均为零
例如
(rounds-to-int? 1) => true
(rounds-to-int? 1.234) => false
(rounds-to-int? 1.01) => false
(rounds-to-int? -1.01) => false
(rounds-to-int? 1.001) => true
(rounds-to-int? -1.001) => true
(rounds-to-int? 1.005) => false
(rounds-to-int? 1.004) => true
感谢任何建议!
理解到不精确性是所提到的数据类型所固有的(例如,大的浮点数甚至不能用整数精度覆盖,所以如果你输入一个大的 Long
这将 return假):
boolean roundsToInt(Number num) {
var fractional = num.doubleValue() - num.intValue();
return Math.abs(fractional) < 0.01; // usual warning about exact representations in binary
}
首先,让我们借用 Clojure 实现一个舍入到特定精度的函数。我稍微修改了 this answer from another SO question.
(defn round-to
"Modified from:
[precision d]
(let [factor (Math/pow 10 (- precision))]
(/ (Math/round (* d factor)) factor)))
接下来我们可以实现我们的自定义舍入逻辑,该逻辑考虑数字是 -1 和 1 之间的小数部分的情况。诀窍是使用 log10 以获得第一个非零的数字数字,也称为最高有效数字。我们可以利用 round-to
函数根据我们的特殊规则对 x
进行舍入。
(defn my-fancy-round
[x]
;; log10 can "count" the number of digits from the decimal point to the most significant digit.
;; A negative log10 indicates that the most significant digit is to the right of the decimal point (aka x is between 0 and 1).
(let [precision (int (Math/log10 (Math/abs x)))]
(round-to (min precision -2) x)))
最后,我们编写 rounds-to-int?
,它将使用我们的自定义逻辑对数字进行舍入,并检查结果是否为整数。
(defn rounds-to-int?
[x]
(let [rounded (my-fancy-round x)]
(= rounded (Math/floor rounded))))
这是我想出并最终使用的解决方案。
(defn rounds-to-int?
[value]
(let [rounded (.setScale (bigdec value) 2 java.math.RoundingMode/HALF_UP)]
(== (int rounded) rounded)))
请参阅函数rel=
in the Tupelo library。它完全符合您的要求:
(is (rel= 123450000 123456789 :digits 4 )) ; .12345 * 10^9
(is (not (rel= 123450000 123456789 :digits 6 )))
(is (rel= 0.123450000 0.123456789 :digits 4 )) ; .12345 * 1
(is (not (rel= 0.123450000 0.123456789 :digits 6 )))
(is (rel= 1 1.001 :tol 0.01 )) ; :tol value is absolute error
(is (not (rel= 1 1.001 :tol 0.0001 )))
我基本上需要 Clojure 中的一个函数,例如 rounds-to-int?
,它的作用相当于:
- 取任意数(整数、浮点数或双精度数)
- 最多保留两位小数(即
1.000001234 => 1.00
) - 判断小数点后是否均为零
例如
(rounds-to-int? 1) => true
(rounds-to-int? 1.234) => false
(rounds-to-int? 1.01) => false
(rounds-to-int? -1.01) => false
(rounds-to-int? 1.001) => true
(rounds-to-int? -1.001) => true
(rounds-to-int? 1.005) => false
(rounds-to-int? 1.004) => true
感谢任何建议!
理解到不精确性是所提到的数据类型所固有的(例如,大的浮点数甚至不能用整数精度覆盖,所以如果你输入一个大的 Long
这将 return假):
boolean roundsToInt(Number num) {
var fractional = num.doubleValue() - num.intValue();
return Math.abs(fractional) < 0.01; // usual warning about exact representations in binary
}
首先,让我们借用 Clojure 实现一个舍入到特定精度的函数。我稍微修改了 this answer from another SO question.
(defn round-to
"Modified from:
[precision d]
(let [factor (Math/pow 10 (- precision))]
(/ (Math/round (* d factor)) factor)))
接下来我们可以实现我们的自定义舍入逻辑,该逻辑考虑数字是 -1 和 1 之间的小数部分的情况。诀窍是使用 log10 以获得第一个非零的数字数字,也称为最高有效数字。我们可以利用 round-to
函数根据我们的特殊规则对 x
进行舍入。
(defn my-fancy-round
[x]
;; log10 can "count" the number of digits from the decimal point to the most significant digit.
;; A negative log10 indicates that the most significant digit is to the right of the decimal point (aka x is between 0 and 1).
(let [precision (int (Math/log10 (Math/abs x)))]
(round-to (min precision -2) x)))
最后,我们编写 rounds-to-int?
,它将使用我们的自定义逻辑对数字进行舍入,并检查结果是否为整数。
(defn rounds-to-int?
[x]
(let [rounded (my-fancy-round x)]
(= rounded (Math/floor rounded))))
这是我想出并最终使用的解决方案。
(defn rounds-to-int?
[value]
(let [rounded (.setScale (bigdec value) 2 java.math.RoundingMode/HALF_UP)]
(== (int rounded) rounded)))
请参阅函数rel=
in the Tupelo library。它完全符合您的要求:
(is (rel= 123450000 123456789 :digits 4 )) ; .12345 * 10^9
(is (not (rel= 123450000 123456789 :digits 6 )))
(is (rel= 0.123450000 0.123456789 :digits 4 )) ; .12345 * 1
(is (not (rel= 0.123450000 0.123456789 :digits 6 )))
(is (rel= 1 1.001 :tol 0.01 )) ; :tol value is absolute error
(is (not (rel= 1 1.001 :tol 0.0001 )))