Java 声明浮点数,为什么默认类型是double?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16369726/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Declaring floats, why default type double?
提问by arynaq
I am curious as to why float literals must be declared as so:
我很好奇为什么浮点文字必须如此声明:
float f = 0.1f;
Instead of
代替
float f = 0.1;
Why is the default type a double, why can't the compiler infer that it is a float from looking at the leftside of the assignment? Google only turns up explanation on what the default values are, not why they are so.
为什么默认类型是双精度型,为什么编译器不能从赋值的左侧推断它是浮点型?谷歌只解释默认值是什么,而不是为什么会这样。
采纳答案by Stephen C
Why is the default type a double?
为什么默认类型是双精度型?
That's a question that would be best asked of the designers of the Java language. They are the only people who know the realreasons why that language design decision was made. But I expect that the reasoning was something along the following lines:
这个问题最好问 Java 语言的设计者。他们是唯一知道做出该语言设计决定的真正原因的人。但我希望推理是这样的:
They needed to distinguish between the two types of literals because they do actually mean different values ... from a mathematical perspective.
他们需要区分这两种类型的文字,因为它们实际上意味着不同的值……从数学的角度来看。
Supposing they made "float" the default for literals, consider this example
假设他们将“浮动”作为文字的默认值,考虑这个例子
// (Hypothetical "java" code ... )
double d = 0.1;
double d2 = 0.1d;
In the above, the d
and d2
would actually have different values. In the first case, a low precision float
value is converted to a higher precision double
value at the point of assignment. But you cannot recover precision that isn't there.
在上面,d
和d2
实际上会有不同的值。在第一种情况下,低float
精度double
值在分配点转换为更高精度值。但是您无法恢复不存在的精度。
I positthat a language design where those two statements are both legal, and mean different things is a BAD idea ... considering that the actual meaning of the first statement is different to the "natural" meaning.
我认为这两个语句都是合法的,并且意味着不同的事物的语言设计是一个坏主意......考虑到第一个语句的实际含义与“自然”含义不同。
By doing it the way they've done it:
通过按照他们的方式来做:
double d = 0.1f;
double d2 = 0.1;
are both legal, and mean different things again. But in the first statement, the programmer's intention is clear, and the second statement the "natural" meaning is what the programmer gets. And in this case:
都是合法的,又意味着不同的东西。但是在第一条语句中,程序员的意图是明确的,第二条语句的“自然”含义是程序员得到的。在这种情况下:
float f = 0.1f;
float f2 = 0.1; // compilation error!
... the compiler picks up the mismatch.
...编译器发现不匹配。
I am guessing using floats is the exception and not the rule (using doubles instead) with modern hardware so at some point it would make sense to assume that the user intends 0.1f when he writes
float f = 0.1;
我猜使用浮点数是例外,而不是现代硬件的规则(使用双精度数代替),因此在某些时候假设用户在编写时打算使用 0.1f 是有意义的
float f = 0.1;
They coulddo that already. But the problem is coming up with a set of type conversion rules that work ... and are simple enough that you don't need a degree in Java-ology to actually understand. Having 0.1
mean different things in different context would be confusing. And consider this:
他们已经可以做到了。但是问题在于提出了一组有效的类型转换规则……而且非常简单,您不需要拥有 Java 学学位就可以真正理解。0.1
在不同的上下文中具有不同的含义会令人困惑。并考虑这一点:
void method(float f) { ... }
void method(double d) { ... }
// Which overload is called in the following?
this.method(1.0);
Programming language design is tricky. A change in one area can have consequences in others.
编程语言设计很棘手。一个领域的改变会对其他领域产生影响。
UPDATEto address some points raised by @supercat.
更新以解决@supercat 提出的一些问题。
@supercat: Given the above overloads, which method will be invoked for method(16777217)? Is that the best choice?
@supercat:鉴于上述重载,method(16777217) 将调用哪个方法?那是最好的选择吗?
I incorrectly commented ... compilation error. In fact the answer is method(float)
.
我错误地评论了...编译错误。事实上答案是method(float)
。
The JLS says this:
JLS 是这样说的:
15.12.2.5. Choosing the Most Specific Method
If more than one member method is both accessible and applicable to a method invocation, it is necessary to choose one to provide the descriptor for the run-time method dispatch. The Java programming language uses the rule that the most specific method is chosen.
...
[The symbols m1 and m2 denote methods that are applicable.]
[If] m2 is not generic, and m1 and m2 are applicable by strictor loose invocation, and where m1 has formal parameter types S1, ..., Sn and m2 has formal parameter types T1, ..., Tn, the type Si is more specific than Ti for argument ei for all i (1 ≤ i ≤ n, n = k).
...
The above conditions are the only circumstances under which one method may be more specific than another.
A type S is more specific than a type T for any expression if S <: T (§4.10).
如果多个成员方法既可访问又适用于方法调用,则必须选择一个成员方法来为运行时方法分派提供描述符。Java 编程语言使用选择最具体的方法的规则。
...
[符号 m1 和 m2 表示适用的方法。]
[如果] m2 不是泛型,并且 m1 和 m2 通过严格或 松散调用适用,并且其中 m1 具有形式参数类型 S1, ..., Sn 和 m2 具有形式参数类型 T1, ..., Tn,类型对于所有 i (1 ≤ i ≤ n, n = k),Si 比 Ti 的参数 ei 更具体。
...
上述条件是一种方法可能比另一种方法更具体的唯一情况。
对于任何表达式,如果 S <: T (§4.10),则类型 S 比类型 T 更具体。
In this case, we are comparing method(float)
and method(double)
which are both applicable to the call. Since float
<: double
, it is more specific, and therefore method(float)
will be selected.
在这种情况下,我们正在比较method(float)
和method(double)
哪些都适用于调用。由于float
<: double
,它更具体,因此method(float)
将被选中。
@supercat: Such behavior may cause problems if e.g. an expression like
int2 = (int) Math.Round(int1 * 3.5)
orlong2 = Math.Round(long1 * 3.5)
gets replaced withint1 = (int) Math.Round(int2 * 3)
orlong2 = Math.Round(long1 * 3)
The change would look harmless, but the first two expressions are correct up to
613566756
or2573485501354568
and the latter two fail above5592405
[the last being completely bogus above715827882
].
@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
or 之前是正确2573485501354568
的,而后两个在上面失败5592405
[最后一个完全是假的715827882
]。
If you are talking about a person making that change ... well yes.
如果你在谈论一个做出这种改变的人......好吧。
However, the compiler won't make that change behind your back. For example, int1 * 3.5
has type double
(the int
is converted to a double
), so you end up calling the Math.Round(double)
.
但是,编译器不会在您背后进行更改。例如,int1 * 3.5
has 类型double
(int
转换为 a double
),因此您最终调用Math.Round(double)
.
As a general rule, Java arithmetic will implicitly convert from "smaller" to "larger" numeric types, but not from "larger" to "smaller".
作为一般规则,Java 算术会隐式地从“较小”转换为“较大”数字类型,但不会从“较大”转换为“较小”。
However, you do still need to be careful since (in your rounding example):
但是,您仍然需要小心,因为(在您的舍入示例中):
the product of a integer and floating point may not be representable with sufficient precision because (say) a
float
has fewer bits of precision than anint
.casting the result of
Math.round(double)
to an integer type can result in conversion to the smallest / largest value of the integer type.
整数和浮点数的乘积可能无法以足够的精度表示,因为(例如) a
float
的精度位比 an 少int
。将 的结果
Math.round(double)
转换为整数类型可能会导致转换为整数类型的最小值/最大值。
But all of this illustrates that arithmetic support in a programming language is tricky, and there are inevitable gotcha's for a new or unwary programmer.
但是所有这些都说明编程语言中的算术支持是棘手的,对于新手或粗心的程序员来说,不可避免地会遇到一些问题。
回答by Dan Tao
Ha, this is just the tip of the iceberg my friend.
哈,这只是我朋友的冰山一角。
Programmers coming from other languages certainly don't mind having to add a little F
to a literal compared to:
F
与以下相比,来自其他语言的程序员当然不介意在文字上添加一点:
SomeReallyLongClassName x = new SomeReallyLongClassName();
Pretty redundant, right?
很多余,对吧?
It's true that you'd have to talk to core Java developers themselves to get more background. But as a pure surface-level explanation, one important concept to understand is what an expressionis. In Java (I'm no expert so take this with a grain of salt), I believe at the compiler level your code is analyzed in terms of expressions; so:
确实,您必须与核心 Java 开发人员交谈才能获得更多背景知识。但作为纯粹的表面级解释,要理解的一个重要概念是表达式是什么。在 Java 中(我不是专家,所以对此持保留态度),我相信在编译器级别,您的代码是根据表达式进行分析的;所以:
float f
has a type, and
有一个类型,并且
0.1f
also has a type (float
).
也有一个类型 ( float
)。
Generally speaking, if you're going to assign one expression to another, the types must agree. There are a few very specific cases where this rule is relaxed (e.g., boxing a primitive like int
in a reference type such as Integer
); but in general it holds.
一般而言,如果要将一个表达式分配给另一个表达式,则类型必须一致。有一些非常特殊的情况可以放宽此规则(例如,将原始int
类型装箱到引用类型中,例如Integer
);但总的来说它是成立的。
It might seem silly in this case, but here's a very similar case where it doesn't seem so silly:
在这种情况下可能看起来很傻,但这里有一个非常相似的情况,它看起来并不那么傻:
double getDouble() {
// some logic to return a double
}
void example() {
float f = getDouble();
}
Now in thiscase, we can see that it makes sense for the compiler to spot something wrong. The value returned by getDouble
will have 64 bits of precision whereas f
can only contain 32 bits; so, without an explicit cast, it's possible the programmer has made a mistake.
现在在这种情况下,我们可以看到编译器发现错误是有意义的。返回的值getDouble
将具有 64 位精度,而f
只能包含 32 位;因此,如果没有明确的强制转换,程序员可能犯了错误。
These two scenarios are clearly different from a humanpoint of view; but my point about expressions is that when code is first broken down into expressions and then analyzed, they are the same.
从人类的角度来看,这两种情况显然不同;但我对表达式的观点是,当代码首先分解为表达式然后进行分析时,它们是相同的。
I'm sure the compiler authors could have written some not-so-clever logic to re-interpret literals based on the types of expressions they're assigned to; they simply didn't. Probably it wasn't considered worth the effort in comparison to other features.
我确信编译器作者可能已经编写了一些不太聪明的逻辑来根据它们分配给的表达式类型重新解释文字;他们根本没有。与其他功能相比,它可能不被认为值得付出努力。
For perspective, plenty of languages are able to do type inference; in C#, for example, you can do this:
从角度来看,许多语言都能够进行类型推断;例如,在 C# 中,您可以这样做:
var x = new SomeReallyLongClassName();
And the type of x
will be inferred by the compiler based on that assignment.
并且x
编译器将根据该分配推断出的类型。
For literals, though, C# is the same as Java in this respect.
但是,对于文字,C# 在这方面与 Java 相同。
回答by Peter Lawrey
float
has a very little precision, and so a more interesting question is; why it is supported at all? There are rare situations when float
can same some memory (if you have millions of them) or you need them for exchanging data with something which expects float.
float
精度很低,所以一个更有趣的问题是;为什么完全支持它?在极少数情况下,float
可以使用相同的内存(如果您有数百万个),或者您需要它们与期望浮动的东西交换数据。
In general, using double
is a better choice, almost as fast for modern PCs, and the memory save is minor compared with the extra precision it gives.
一般来说,使用double
是更好的选择,对于现代 PC 来说几乎一样快,而且与它提供的额外精度相比,内存节省是微不足道的。
Java doesn't look at the left side in any situation to see how a value is used. e.g. the return type of a method is not part of the signature. It will cast down implicitly in some cases for assignment and operator assignments, but this is usually to keep some compatibility with C and is rather ad hoc IMHO.
Java 在任何情况下都不会查看左侧以了解如何使用值。例如,方法的返回类型不是签名的一部分。在某些情况下,它会隐式地转换为赋值和运算符赋值,但这通常是为了与 C 保持一定的兼容性,并且是临时的,恕我直言。