为什么(double)0.6f > (double)(6/10f)?

10

这是在我的电脑上所发生的:

(double)(float)0.6
= 0.60000002384185791

(double)0.6f
= 0.60000002384185791

(double)(6/10f)
= 0.6

(double)(float)(6/10f)
= 0.6

6/10f也是浮点数,为什么它可以精确地为0.6呢?
在我的想法中(double)(6/10f)应该也是0.60000002384185791。 有人能帮忙解释一下吗?谢谢!


顺便提一下,在Java中,以上所有的结果都是0.6000000238418579。 - Thomas Mueller
我正在使用即时窗口。我在程序中进行了测试,它们不相等。所以这不是格式化的问题。 - zhy2002
2
@Thomas,IEEE754双精度让您可以使用0x3fe3333333333333值更接近于0.6。 - paxdiablo
1
总的来说,千万不要试图期望浮点数/双精度浮点数相等。 - Yuliy
可能是为什么浮点数不准确?的重复问题。 - Jonathan Hall
显示剩余3条评论
3个回答

5
首先,需要记住的是0.6不能准确地表示为float,但它可以准确地表示为double(如果您不清楚为什么0.6不能准确表示为浮点数,请尝试此链接了解浮点运算的不准确性)。
你看到上面的行为是由编译器决定的 - 如果你用反射器查看已编译的程序集,那么这里正在发生的事情会更加清晰。
更新:我已更改代码,使其不使用Console.WriteLine,因为我意识到编译器会为您选择一个重载,这会混淆情况。)
// As written in source
var j = (double)(float)0.6;
var k = (double)0.6f;
var l = (double)(6/10f);
var m = (double)(float)(6/10f);

// Code as seen by Reflector
double j = 0.60000002384185791;
double k = 0.60000002384185791;
double l = 0.6;
double m = 0.6;

为什么编译器选择以这种特定的方式编译对我来说是无法理解的(顺便提一下,这全部都是在关闭优化的情况下完成的)。

还有一些有趣的情况:

// Code
var a = 0.6;
var b = (double)0.6;
var c = 0.6f;
var d = (float)0.6;

var e = 6 / 10;
var f = 6 / (10f);
var g = (float)(6 / 10);
var h = 6 / 10f;
var i = (double)6 / 10;
// Prints out 0.60000002384185791

double n = (float)0.6;
double o = f;

// As seen by Reflector
double a = 0.6;
double b = 0.6;
float c = 0.6f;
float d = 0.6f;

int e = 0;
float f = 0.6f;
float g = 0f;
float h = 0.6f;
double i = 0.6;

double n = 0.60000002384185791;
double o = f;

编译器似乎只在一些特殊情况下使用上述技巧,它为什么只在转换为双精度时这样做完全超出了我的理解!
其余时间,它似乎会进行一些巧妙的操作,使浮点运算看起来像正常工作,而实际上通常不会这样。

有趣的是,如果您使用float,所有值都将打印0.6(如在ideone上看到的那样,也已在Visual C#中进行了测试)。 - NullUserException
谢谢大家,我认为这确实是一个编译器特定的问题。 - zhy2002
同时我发现:float float3 = 0.6f; int int1 = 6; float1 = 10; Assert.IsTrue(int1 / float1 >= float3);//失败了,在我的代码中float3是一个参数! - zhy2002
4
0.6无法准确表示为双精度浮点数。 - Rick Regan
2
@Rick是正确的,这个答案是错误的。3/5在二进制中无限重复,因此不能准确地表示为“double”或任何有限的二进制数。 - Matthew Flaschen

2
似乎是对结果进行了四舍五入。你是否以必要的精度显示结果?您可以使用Jon Skeet的这个C#类来获得打印出结果的确切数字表示形式。
请注意,ToString()不总是打印所有数字,Visual Studio调试器也不会。

1
如果我是一个赌徒的话,我会说区别在于强制转换发生的位置。在后两个示例中(带有6/10f的示例),有两个字面量都是整数(整数6和浮点数10.00000000...)。除法似乎是在强制转换后发生的,至少在您使用的编译器中是这样。在前两个示例中,您有一个小数浮点文字(0.6),它无法在浮点数的尾数中足够表达为二进制值。将该值强制转换为双精度浮点数无法修复已经造成的损害。
在产生完全一致结果的环境中,除法发生在强制转换为双精度浮点数之前(6将被强制转换为浮点数以匹配10,除法在浮点空间中执行,然后将结果强制转换为双精度浮点数)。

你可以随时使用ildasm并查看中间生成的程序集,来了解发生了什么。 - Stan Rogers

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接