我希望生成以下代码:“ 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");
}
这是什么原因?
我希望生成以下代码:“ 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");
}
这是什么原因?
这是因为最接近 1.3 的浮点值与最接近 1.3 的双精度值不同。这两个值都不会恰好是1.3 - 不能以非重复二进制表示形式精确表示。
为了对为什么会发生这种情况给出不同的理解,假设我们有两个十进制浮点类型 -decimal5
和decimal10
,其中数字表示有效数字的数量。现在假设我们尝试将“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
。
浮点数是单精度浮点数。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
),因此在比较相等性时可能会得到意想不到的结果。
永远不要检查浮点数之间的相等性。具体来说,要回答您的问题,数字 1.3 很难表示为二进制浮点,并且双精度和浮点表示不同。
阅读这篇文章。
上面的文章通过示例清楚地说明了使用 double 和 float 类型时的场景。
float a=1.3f;
double b=1.3;
此时,您有两个包含实数 1.3 的二进制近似值的变量。第一个近似值精确到大约 7 个十进制数字,第二个近似值精确到大约 15 个十进制数字。
if(a==b) {
表达式a==b
分两个阶段进行评估。首先,通过填充二进制表示,将 的值a
从 a 转换为 a float
。double
作为 Real 1.3 的表示,结果仍然只精确到大约 7 个十进制数字。接下来,您将比较两种不同的近似值。由于它们不同,结果a==b
是false
。
有两个教训要学习:
浮点(和双精度)文字几乎总是近似值;例如,对应于文字1.3f
的实际数字不完全等于实数1.3
。
每次进行浮点计算时,都会出现错误。这些错误往往会累积。因此,当您比较浮点数/双精度数时,使用简单的“==”、“<”等通常是错误的。Instead you should use |a - b| < delta
where delta
is chosen appropriately. (并且弄清楚什么是合适delta
的也不总是直截了当的。)
您应该参加过数值分析课程:-)
问题是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 的语义不同float
double
double
float
float
to double
,而 Java 默认选择的行为(强制float
转换 to double
)通常在语义上是错误的。
实际上 float 和 double 都不能存储 1.3。我是认真的。仔细观看此视频。
https://www.youtube.com/watch?v=RtHKwsXuRkk&index=50&list=PL6pxHmHF3F5JPdnEqKALRMgogwYc2szp1