[英]How to compare correctly long and float primitives?
出於某種原因,我需要比較兩個原語:long 和 float。
我可以為此使用以下代碼嗎?
long a = 111L;
float b = 111.1f
if (a > b) {
...
}
我知道,可以使用 epsilon 值等將浮點數和浮點數與某些精度進行比較。
但是我怎樣才能更正確地對我的案例進行比較?
謝謝大家。
您可以將它們都包裝在BigDecimal
,然后將它們進行比較:
long a = 111L;
float b = 111.1f;
BigDecimal first = new BigDecimal(a);
BigDecimal second = new BigDecimal(b, MathContext.DECIMAL32);
if (first.compareTo(second) > 0) { ... }
為了理解為什么我們有興趣在同一類型下擁有兩個操作數,讓我們深入研究JLS 5.6.2 Binary Numeric Promotion :
當運算符將二進制數字提升應用於一對操作數時,每個操作數必須表示一個可轉換為數字類型的值,以下規則依次適用:
如果任何操作數是引用類型,則需要進行拆箱轉換(第 5.1.8 節)。
擴展原語轉換(第 5.1.2 節)用於轉換一個或兩個操作數,如以下規則所指定:
如果任一操作數的類型為
double
,則另一個將轉換為double
。否則,如果任一操作數的類型為
float
,則另一個將轉換為float
。否則,如果任一操作數的類型為
long
,則另一個將轉換為long
。否則,兩個操作數都被轉換為
int
類型。
有了這個,我們可以得出結論,對於比較a > b
, long
操作數將被隱式提升為float
。 但是,這可能會導致精度損失,如JLS 5.1.2 Widening 原語轉換中所述:
int
或long
值到float
或 long 值到 double 的擴展轉換可能會導致精度損失 - 也就是說,結果可能會丟失值的一些最低有效位。 在這種情況下,生成的浮點值將是整數值的正確舍入版本,使用 IEEE 754 舍入到最近模式(第 4.2.4 節)。
如果你想使用 epsilon 你可以這樣做
// allow for the smallest rounding error
if (a > b + Math.sign(b) * Float.ulp(b))
或者
// assume six digits of precision
static final double ERR = 1e-6;
// allow for the smallest rounding error
if (a > b + Math.sign(b) * b * ERR)
如果您可以假設非負數,則可以刪除 Math.sign(b)。
但是,在這種情況下使用 BigDecimal 可能更清楚,請參閱@kocko 的回答。
順便說一句:可以提高准確性的最簡單的更改是使用double
而不是float
。 double
實際上准確度高了 50 億倍,除非您擁有數十億個,否則您使用的提取內存將無關緊要。
Java 支持直接比較 long 和 float。 但是,有一些注意事項:
如果比較 long 和 float,則 long 將轉換為 float,因為 float 被認為是比 long 更寬的類型。 但是,這種轉換可能會失去精度:long 值將被轉換為最接近的浮點值; 所以長期a
和浮子b
, a > b
雖然可能是假的a
大於b
,如果b
實際上是最接近浮點值b
。
每個 long 和 float 值都可以由BigDecimal
表示,因此您還可以使用BigDecimal
類來比較 long 和 a float:
long a = ...;
float b = ...;
if (BigDecimal.valueOf(a).compareTo(BigDecimal.valueOf(b)) > 0) {
...
}
輕松使用此代碼:
Double.compare(longVar * 1., floatVar)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.