Java 如何正确比较long和float原语?

Java 如何正确比较long和float原语?,java,primitive-types,Java,Primitive Types,出于某种原因,我需要比较两个原语:long和float 我可以为此使用以下代码吗 long a = 111L; float b = 111.1f if (a > b) { ... } 我知道,用epsilon值等可以比较float和float的精确度 但是,如何才能更正确地对我的案例进行比较呢 多亏了大家。您可以将它们都包装在BigDecimal中,并对它们进行比较: long a = 111L; float b = 111.1f; BigDecimal first = new B

出于某种原因,我需要比较两个原语:long和float

我可以为此使用以下代码吗

long a = 111L;
float b = 111.1f

if (a > b) {
  ...
}
我知道,用epsilon值等可以比较float和float的精确度

但是,如何才能更正确地对我的案例进行比较呢


多亏了大家。

您可以将它们都包装在
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) { ... }
为了理解为什么我们对同一类型下的两个操作数都感兴趣,让我们深入研究一下:

当运算符将二进制数字提升应用于一对操作数(每个操作数必须表示可转换为数字类型的值)时,以下规则依次适用:

  • 如果任何操作数属于引用类型,则将对其进行取消装箱 转换(§5.1.8)

  • 加宽原语转换(§5.1.2)适用于转换 或按以下规则指定的两个操作数:

    • 如果任一操作数的类型为双精度,则另一个操作数将转换为
      double

    • 否则,如果其中一个操作数的类型为
      float
      ,则转换另一个操作数 到
      浮动

    • 否则,如果任一操作数的类型为
      long
      ,则转换另一个操作数 到

    • 否则,两个操作数都将转换为类型
      int

  • 由此我们可以得出结论,对于比较
    a>b
    long
    操作数将隐式提升为
    float
    。但是,这最终可能会导致精度损失,如中所述:

    int
    long
    值到
    float
    或long的加宽转换 值加倍,可能会导致精度损失-即结果 可能会丢失一些值的最低有效位。在这个 在这种情况下,生成的浮点值将被正确舍入 整数值的版本,使用IEEE 754四舍五入到最近模式 (§4.2.4)


    如果你想用ε,你可以这样做

    // allow for the smallest rounding error
    if (a > b + Math.sign(b) * Float.ulp(b))
    

    如果你能假设非负数,你可以放弃数学符号(b)

    然而,在这种情况下使用BigDecimal可能更清楚,请参见@kocko的答案


    顺便说一句:提高准确性的最简单的改变是使用
    double
    而不是
    float
    double
    的准确度实际上要高出5亿倍,除非你拥有数十亿倍的内存,否则你使用的提取内存将无关紧要。

    Java支持直接比较长和浮点。但是,有一些警告:

    如果比较long和float,long将转换为float,因为float被认为是比long更宽的类型。但是,此转换可能会失去精度:长值将转换为最近的浮点值;因此,对于长
    a
    和浮点
    b
    a>b
    可能为假,尽管
    a
    大于
    b
    ,如果
    b
    实际上是最接近
    b
    的浮点值

    每个long和float值都可以用
    BigDecimal
    表示,因此您也可以使用
    BigDecimal
    类来比较long和float:

    long a = ...;
    float b = ...;
    if (BigDecimal.valueOf(a).compareTo(BigDecimal.valueOf(b)) > 0) {
        ...
    }
    
    轻松使用此代码:

    Double.compare(longVar * 1., floatVar)
    

    我给出了一个可以帮助你的答案。看看这个,你如何定义“正确”?上面的代码将告诉您,
    111
    不大于
    111.1f
    ,这就是我自己所说的“正确”。所以我看不出有什么不同的理由……你可能会考虑使用一个<代码>浮点< /代码>的适当舍入。代码>新的BigDecimal(b,MathContext.DECIMAL32)将值与
    进行比较时,
    浮点
    双精度
    的精度差异不太可能相关。更重要的是,具有较大绝对值的
    long
    无法用
    float
    精确表示,但是,如果源值的精度不高,将其转换为
    double
    并不能神奇地增加精度。但我看不出epsilon如何改善
    比较。使用ε对等式测试有意义,但对排序没有意义…@Holger你对比较有什么怀疑。当然a:100>b:10,即使误差为1。误差必须为0才能在误差范围内说100>10吗?好吧,让我们继续看
    b=10
    示例。原始代码是
    a>b
    ,因此有效地执行
    a>10.0
    。现在将其替换为
    a>b+Math.signum(b)*Math.ulp(b)
    ,这意味着您将有效地执行
    a>10.000000000000002
    。为什么你认为
    a>10.000000000000002
    a>10.0
    有所改进?如果
    a
    long
    也没有区别,但是如果
    a
    是浮点值,引入ε意味着,如果
    a
    恰好是
    10.00000000000001
    ,则条件是
    false
    。我看不到这里的改进。我误读了你的评论-所以你说你认为代码<代码> 100000×100L不大于<代码> 100000万0f <代码>作为改进?我不知道。如前所述,当有人测试平等性时,这是有意义的。然后,有一些值不被认为是更大或更小(换句话说,根据ε相等),但对于单个“大于”运算符,不可能在没有上下文的情况下说哪个行为更好。@Holger我同意上下文确实重要。这就是为什么我建议采取保守的方法。即使在相等的情况下,也不能说两个数相等,只能说两个数不相等。
    Double.compare(longVar * 1., floatVar)