【问题标题】:java Float: == equals compareTojava浮点数:==等于compareTo
【发布时间】:2012-10-25 14:49:51
【问题描述】:

我有 2 个问题。

1) 有人告诉我,在比较两个 FloatDouble 数据时,请使用 compareTo 而不是 equals。我不知道原因。是否有任何示例表明使用 equals 会导致错误?

2) 见此代码:

  float f2=(float)1.123450;
  Float f3=new Float(1.123450);

  System.out.println(f3==f2);   // result is true

我认为使用== 意味着两个数据指向同一个内存地址。但是f3f2 有相同的地址吗? new Float(...) 不会创建新空间吗?

【问题讨论】:

标签: java equals compareto


【解决方案1】:

如果两个参数都是引用类型,那么== 将测试内存位置。但是,如果==(或!=)的参数之一是数字,而另一个可转换为数字(使用取消装箱),则通过在取消装箱后比较数值来完成比较。因此,这种情况下的比较是基于浮点值(在这种情况下是相同的)进行的。详情请见Java Language Specification §15.21.1

但请注意,Float.NaN == Float.NaNfalse

【讨论】:

  • 另请注意,一般来说,您希望通过查看它们是否非常接近来比较两个浮点数/双精度数,而不是它们是否完全相等。这是由于浮点数可能累积的舍入误差,尤其是当十进制数不能精确地表示为浮点数时(例如 0.1)。
  • @yshavit - 是的,这是一个很好的观点,但在讨论 equalscompareTo== 之间的区别时却忽略了一个值。 (然而,我现在看到彼得改变了他的回答,把注意力集中在那个确切的点上。)
【解决方案2】:

一般来说== 很好,除非您需要处理舍入或算术错误,在这种情况下 compareTo() 对您没有多大帮助。

如果你想在一个错误中比较两个双打,你可以使用

if (Math.abs(a - b) < ERR) // within error.

或相对错误

if (Math.abs(a - b) < ERR * (Math.abs(a) + Math.abs(b))/2) // within error.

或在四舍五入内,该因子是否通常是 10 的幂,例如 10000 意味着小数点后 4 位。

if (Math.round(a * factor) == Math.round(b * factor)) // within a multiple

【讨论】:

  • From the docs for Float.equals(): "如果f1f2都代表Float.NaN,那么equals方法返回true,即使Float.NaN==Float.NaN的值是@987654331 @。”
【解决方案3】:

在 Java 中,您有两种(主要)类型,

  1. 原语
  2. 参考

    当且仅当它们的类型为 doublefloatintlong 等时,基本类型才被视为基本类型。

    引用类型是任何使用“对象”来存储数据的类型。当您在课堂上创建时,您实际上是在“变相”创建引用类型。引用类型的一些示例是 StringDoubleInteger 等。

    所以,就类型而言,当您比较 float xFloat y 时,您实际上是在比较两种不同的类型!

    在 Java 中,== 的运算符大部分时间只比较相同类型的对象。然而,有时该语言将允许比较其他类型,例如x == yx 是原始类型而y 是引用类型(如上定义)的情况下。

    当执行xy 之间的比较时,会执行称为装箱和拆箱的操作。但是要理解装箱,就必须了解原始类型和引用类型在内存语义方面的区别。 (不要让那吓到你!)

    原始类型存储在称为堆栈的内存位置中,这在碎片访问方面速度很快且不是很灵活。这很容易。

    但是引用类型是不同的:当引用类型实例化使用new操作符(在执行Float x = &lt;something&gt;;时被隐式调用——也就是说,Float x = &lt;something&gt;;变成@ 987654339@) 所以当Float 或其他引用类型被实例化时,对象被创建并存储在堆上,但指向该对象(在堆上)的指针存储在堆栈中。

    这意味着为了检索存储在x 中的值,计算机必须使用存储在堆栈中的地址,并转到堆中的那个内存地址。

    我们使用堆来存储引用类型,因为堆栈不太擅长所谓的“动态内存分配”,即在不关心周围其他对象的情况下分配和释放内存的地方。

    现在进行装箱和拆箱:

    装箱(也称为包装)是获取原始类型对象并将其存储为引用类型的过程(因此float x 变为Float x),因此这两个对象相同 类型。 (这有点像用纸包圣诞礼物)所以,在幕后,Integer k = 6 是一种装箱(自动装箱)

    另一方面,拆箱与拆箱相反,因此您可以将其称为拆箱。拆箱采用“装箱”对象并将其从引用类型带回原始类型,因此语句可以毫不费力地工作。:

    Integer k = 6; //Boxing

    int m = k; //Unboxing

    就您的问题而言,这真正意味着什么:在您发布的代码中,发生了自动装箱和拆箱,这使得该声明有效。 JVM 足够聪明,可以按照你的意思去做——但这并不意味着你应该养成它的常规习惯,因为装箱和拆箱会对代码的性能产生严重影响!

    另外,如果 xy 都是 Float 类型,那么您将比较引用!

    祝你好运!

【讨论】:

    猜你喜欢
    • 2012-06-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-06
    • 1970-01-01
    • 2018-11-22
    • 1970-01-01
    相关资源
    最近更新 更多