我很好奇为什么浮动文字必须如此声明:
float f = 0.1f;
而不是
float f = 0.1;
为什么默认类型为double,为什么编译器不能通过查看赋值的左侧来推断它是浮点数?谷歌只会出现关于默认值是什么的解释,而不是为什么会这样。
答案 0 :(得分:35)
为什么默认类型为double?
这个问题最适合Java语言的设计者。他们是唯一知道为什么做出语言设计决定的真实原因的人。但我希望推理有以下几点:
他们需要区分两种类型的文字,因为从数学的角度来看,它们确实意味着不同的价值观。
假设他们将“浮动”作为文字的默认值,请考虑此示例
// (Hypothetical "java" code ... )
double d = 0.1;
double d2 = 0.1d;
在上文中,d
和d2
实际上会有不同的值。在第一种情况下,低精度float
值在赋值时转换为更高精度double
值。但你无法恢复那里没有的精确度。
我 posit 一个语言设计,这两个陈述都是合法的,意味着不同的东西是一个不好的想法......考虑到第一个陈述的实际含义不同于“自然“意思。
按照他们的方式去做:
double d = 0.1f;
double d2 = 0.1;
既合法,又意味着不同的东西。但是在第一个声明中,程序员的意图是明确的,第二个声明是“自然”的意思是程序员得到的。在这种情况下:
float f = 0.1f;
float f2 = 0.1; // compilation error!
......编译器发现不匹配。
我猜测使用浮点数是例外而不是规则(使用双打而不是)与现代硬件,所以在某些时候,假设用户在写
float f = 0.1;
他们可以已经这样做了。但问题是提出了一组有效的类型转换规则......而且很简单,你不需要Java -ology的学位来实际理解。让0.1
在不同的背景下意味着不同的事情会令人困惑。并考虑一下:
void method(float f) { ... }
void method(double d) { ... }
// Which overload is called in the following?
this.method(1.0);
编程语言设计很棘手。一个领域的变化会对其他领域产生影响。
更新以解决@supercat引发的一些问题。
@supercat:鉴于上述重载,将为方法(16777217)调用哪个方法?这是最好的选择吗?
我错误地评论了...编译错误。实际上答案是method(float)
。
JLS说:
15.12.2.5. Choosing the Most Specific Method
如果多个成员方法都可访问且适用于a 方法调用,有必要选择一个提供 运行时方法调度的描述符。 Java编程 language使用选择最具体方法的规则。
...
[符号m1和m2表示适用的方法。]
[If] m2不是通用的,m1和m2适用于 strict 或 松散调用,其中m1具有形式参数类型S1,...,Sn 并且m2具有形式参数类型T1,...,Tn,Si类型更多 比所有i(1≤i≤n,n = k)的参数ei特定于Ti。
...
以上条件是一种方法的唯一情况 可能比另一个更具体。
如果S<:T,则对于任何表达式,类型S比类型T更具特异性 (§4.10)。
在这种情况下,我们正在比较适用于此次通话的method(float)
和method(double)
。由于float
&lt ;: double
,因此更具体,因此将选择method(float)
。
@supercat:这种行为可能会导致问题,例如:一种表达
int2 = (int) Math.Round(int1 * 3.5)
或long2 = Math.Round(long1 * 3.5)
替换为int1 = (int) Math.Round(int2 * 3)
或long2 = Math.Round(long1 * 3)
这种变化看起来无害,但前两个表达方式是 最多可以纠正
613566756
或2573485501354568
以及后两者 失败超过5592405
[最后一次完全是假的715827882
]。
如果你在谈论一个人做出这种改变......好吧。
但是,编译器不会在您的背后进行更改。例如,int1 * 3.5
的类型为double
(int
已转换为double
),因此您最终会调用Math.Round(double)
。
作为一般规则,Java算法将隐式地从“较小”转换为“较大”数字类型,但不会从“较大”转换为“较小”。
但是,你仍然需要小心,因为(在你的舍入示例中):
整数和浮点的乘积可能无法以足够的精度表示,因为(例如)float
的精度比int
的精度更低。
将Math.round(double)
的结果转换为整数类型可导致转换为整数类型的最小/最大值。
但是所有这些都表明编程语言中的算术支持是棘手的,并且对于一个新的或不谨慎的程序员来说存在不可避免的问题。
答案 1 :(得分:2)
来自其他语言的程序员当然不介意在文字中添加一点F
而不是:
SomeReallyLongClassName x = new SomeReallyLongClassName();
非常多余,对吧?
确实,您必须与核心Java开发人员交谈以获得更多背景知识。但作为纯粹的表面层次解释,要理解的一个重要概念是表达式是什么。在Java中(我不是专家,所以请耐心等待),我相信在编译器级别,您的代码将根据表达式进行分析;这样:
float f
有一个类型,
0.1f
也有类型(float
)。
一般来说,如果您要将一个表达式分配给另一个表达式,则必须同意这些类型。有一些非常具体的情况可以放宽这个规则(例如,在诸如int
之类的引用类型中装入像Integer
这样的原语;但一般情况下它都有。
在这种情况下看起来似乎很愚蠢,但这里的情况非常相似,似乎并不那么愚蠢:
double getDouble() {
// some logic to return a double
}
void example() {
float f = getDouble();
}
现在在这个的情况下,我们可以看到编译器发现错误是有意义的。 getDouble
返回的值将具有64位精度,而f
只能包含32位;所以,如果没有明确的演员,程序员可能犯了错误。
这两种情况明显不同于人的观点;但我对表达式的观点是,当代码首先被分解为表达式然后进行分析时,它们是相同的。
我确信编译器作者可能已经编写了一些不那么聪明的逻辑来根据它们分配给它们的表达式来重新解释文字;他们根本没有。与其他功能相比,可能不值得付出努力。
从视角来看,很多语言都可以进行类型推断;例如,在C#中,您可以这样做:
var x = new SomeReallyLongClassName();
编译器将根据该分配推断出x
的类型。
但是,对于文字,C#在这方面与Java相同。
答案 2 :(得分:0)
float
的精确度非常低,所以一个更有趣的问题是:为什么它得到支持?在极少数情况下,float
可以使用相同的内存(如果您有数百万个内存),或者您需要它们与期望浮动的内容交换数据。
一般来说,使用double
是一个更好的选择,几乎与现代PC一样快,与其提供的额外精度相比,内存节省较小。
Java在任何情况下都不会查看左侧以查看值的使用方式。例如方法的返回类型不是签名的一部分。它会在某些情况下隐含地用于赋值和运算符赋值,但这通常是为了保持与C的一些兼容性,而且是临时的恕我直言。