6

我希望生成以下代码:“ Both are equal”,但我得到了“ Both are NOT equal”:

float a=1.3f;
double b=1.3;
if(a==b)
{
 System.out.println("Both are equal");
}
else{
 System.out.println("Both are NOT equal");
}

这是什么原因?

4

7 回答 7

28

这是因为最接近 1.3 的浮点值与最接近 1.3 的双精度值不同。这两个值都不会恰好是1.3 - 不能以非重复二进制表示形式精确表示。

为了对为什么会发生这种情况给出不同的理解,假设我们有两个十进制浮点类型 -decimal5decimal10,其中数字表示有效数字的数量。现在假设我们尝试将“athird”的值分配给它们两个。你最终会得到

decimal5 oneThird = 0.33333
decimal10 oneThird = 0.3333333333

显然,这些值不相等。这里完全一样,只是涉及的基础不同。

但是,如果您将值限制为不太精确的类型,您会发现在这种特殊情况下它们相等的:

double d = 1.3d;
float f = 1.3f;
System.out.println((float) d == f); // Prints true

但是,不能保证是这种情况。有时,从十进制文字到双精度表示的近似值,然后是该值到浮点表示的近似值,最终不如直接十进制到浮点近似值准确。这个 1.0000001788139343 的一个例子(感谢 stephentyrone 找到这个例子)。

更安全的是,您可以在双精度之间进行比较,但float在原始赋值中使用文字:

double d = 1.3f;
float f = 1.3f;
System.out.println(d == f); // Prints true

在后一种情况下,这有点像说:

decimal10 oneThird = 0.3333300000

但是,正如评论中指出的那样,您几乎可以肯定应该将浮点值与 == 进行比较。这几乎从来都不是正确的事情,因为正是这种事情。通常,如果您想比较两个值,您可以使用某种“模糊”相等比较,检查这两个数字是否“足够接近”以达到您的目的。有关更多信息,请参阅Java 陷阱:双页。

如果您确实需要检查绝对相等,这通常表明您应该首先使用不同的数字格式 - 例如,对于您可能应该使用的财务数据BigDecimal

于 2009-10-01T06:39:59.613 回答
12

浮点数是单精度浮点数。double 是双精度浮点数。更多细节在这里: http: //www.concentric.net/~Ttwang/tech/javafloat.htm

注意:检查浮点数的精确相等性是一个坏主意。大多数时候,您希望根据增量或容差值进行比较。

例如:

float a = 1.3f;
double b = 1.3;
float delta = 0.000001f;
if (Math.abs(a - b) < delta)
{
    System.out.println("Close enough!");
}
else
{
    System.out.println("Not very close!");
}

有些数字不能用浮点数精确表示(例如0.01),因此在比较相等性时可能会得到意想不到的结果。

于 2009-10-01T06:43:30.837 回答
2

永远不要检查浮点数之间的相等性。具体来说,要回答您的问题,数字 1.3 很难表示为二进制浮点,并且双精度和浮点表示不同。

于 2009-10-01T06:41:41.747 回答
2

阅读这篇文章

上面的文章通过示例清楚地说明了使用 double 和 float 类型时的场景。

于 2009-10-01T06:44:46.577 回答
2
float a=1.3f;
double b=1.3; 

此时,您有两个包含实数 1.3 的二进制近似值的变量。第一个近似值精确到大约 7 个十进制数字,第二个近似值精确到大约 15 个十进制数字。

if(a==b) { 

表达式a==b分两个阶段进行评估。首先,通过填充二进制表示,将 的值a从 a 转换为 a floatdouble作为 Real 1.3 的表示,结果仍然只精确到大约 7 个十进制数字。接下来,您将比较两种不同的近似值。由于它们不同,结果a==bfalse

有两个教训要学习:

  1. 浮点(和双精度)文字几乎总是近似值;例如,对应于文字1.3f的实际数字不完全等于实数1.3

  2. 每次进行浮点计算时,都会出现错误。这些错误往往会累积。因此,当您比较浮点数/双精度数时,使用简单的“==”、“<”等通常是错误的。Instead you should use |a - b| < deltawhere deltais chosen appropriately. (并且弄清楚什么是合适delta的也不总是直截了当的。)

  3. 您应该参加过数值分析课程:-)

于 2009-10-01T06:46:01.117 回答
0

问题是Java(以及唉.NET)对于一个float值是代表一个单一的精确数字量还是一个数量范围是不一致的。如果 afloat被认为表示形式的精确数字量Mant * 2^Exp,其中Mant是整数 0 到 2^25 并且Exp是整数),则尝试将任何不属于该形式的数字转换为float应该抛出异常。double如果它被认为代表“上面形式的某些特定表示可能被认为是最好的数字轨迹”,那么即使对于不属于上述形式的值,双浮点转换也是正确的 [castingdouble最能代表 a 数量的那个float几乎总是会产生float尽管在某些极端情况下(例如,从 8888888.500000000001 到 8888888.500000000932 范围内的数字量) ,所选择的数字量可能比实际数字量float的最佳可能表示形式差几分之一。float

打个比方,假设两个人各有一个十厘米长的物体并测量它。Bob 使用一套昂贵的口径并确定他的对象是 3.937008" 长。Joe 使用卷尺并确定他的对象是 3 15/16" 长。物体大小一样吗?如果将 Joe 的测量值转换为百万分之一英寸 (3.937500"),则测量值将显示不同,但如果将 Bob 的测量值转换为最接近的 1/256" 分数,它们将显示相等。虽然前者的比较可能看起来更“精确”,但后者往往更有意义。如果 3 15/16 英寸,乔的测量值并不真正意味着 3.937500 英寸——它的意思是“使用卷尺无法与 3 15/16 区分的距离”。而 3.937008" 是,

不幸的是,即使使用较低精度比较测量值更有意义,Java 的浮点比较规则假定 afloat表示单个精确数字量,并在此基础上执行比较。虽然在某些情况下这是有用的(例如,知道double通过将某个值转换为float和返回到所产生的特定值是否与起始值匹配),一般来说,和之间的double直接相等比较是没有意义的。即使 Java 不需要它,也应该始终将浮点相等比较的操作数转换为相同类型。在比较之前将to转换为与转换 the 的语义不同floatdoubledoublefloatfloatto double,而 Java 默认选择的行为(强制float转换 to double)通常在语义上是错误的。

于 2013-06-07T17:10:49.040 回答
0

实际上 float 和 double 都不能存储 1.3。我是认真的。仔细观看此视频。

https://www.youtube.com/watch?v=RtHKwsXuRkk&index=50&list=PL6pxHmHF3F5JPdnEqKALRMgogwYc2szp1

于 2017-04-09T06:22:44.647 回答