2013-05-04 2 views
32

나는 부동 소수점 리터럴이 너무로 선언해야합니다 이유에 대해 궁금합니다 :수레 선언, 왜 기본 유형 double인가?

float f = 0.1f; 

대신

float f = 0.1; 

왜 기본 수는 컴파일러하지 유추 왜 두 번을 입력한다 과제의 왼쪽을 바라 보는 부유물? Google은 기본값이 무엇인지에 대한 설명 만 제시합니다. 왜 그런지는 설명하지 않습니다.

+2

[이 페이지] (http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.2.3)에 두 배에 대한 정보가 있습니다. 수레와 IEEE 754를 더 잘 이해하는 데 도움이됩니다. – RyPope

+0

나는 항상이 사실을 궁금해했습니다. 문제는 컴파일러가 어떻게 10 진수를 감지하는지에 달려 있습니다. 그것은 이중 리터럴 기본값으로 나타납니다 ... 난 항상 어쨌든 복식을 사용하여 개인적인 문제가 있었 (왜 과학이나 공학 분야에서 너무 많은 정밀도가 필요하십니까?) – Singular1ty

+0

나는 거기에 컴파일러에 단점이 없을 것이라고 생각 그것을 인식 할 수 있습니다. 그래서 내가 볼 수있는 한, 그 뒤에는 진짜 이유가 없습니다. 급속한 인터넷 검색은 나에게도 유효한 이유를 지적하지 못했지만 잘못 될 수 있습니다. – DPM

답변

28

왜 기본 유형이 이중입니까?

Java 언어 디자이너에게 가장 좋은 질문입니다. 그들은 해당 언어 디자인 결정이 이루어진 이유가 인 유일한 사람입니다. 그러나 나는 그 추론이 다음과 같은 라인을 따라야한다고 생각한다 :

두 가지 유형의 리터럴은 실제로 수학적 관점에서 다른 값을 의미하기 때문에 구별 할 필요가 있었다. 그들은 "부동"리터럴의 기본을 만든 가정 할

, 다른 값을 실제로있을 것입니다 예를

// (Hypothetical "java" code ...) 
double d = 0.1; 
double d2 = 0.1d; 
위에서

dd2을 고려하십시오. 첫 번째 경우, 낮은 정밀도 인 float 값은 할당 시점에 더 높은 정밀도 double 값으로 변환됩니다. 그러나 거기에없는 정밀도는 복구 할 수 없습니다.

나는 그 두 문은 모두 법적, 그리고 다른 의미를 언어 디자인이 나쁜 생각 ... 첫 번째 명령문의 실제 의미는 "자연"을 의미하는 다른 것을 고려하고있다을 단정 . 그것을 그들이 그것을 한 적이 방식을 수행함으로써

:

double d = 0.1f; 
double d2 = 0.1; 

은 모두 합법적 다시 다른 것을 의미한다. 그러나 첫 번째 문장에서 프로그래머의 의도는 명확하고 두 번째 문장은 "자연스러운"의미는 프로그래머가 얻는 것입니다. 그리고이 경우 :

float f = 0.1f; 
float f2 = 0.1; // compilation error! 

... 컴파일러가 불일치를 선택합니다. 그가 float f = 0.1;

을 쓸 때 사용자가 0.1F을 있다는 것을 가정하는 것이 나을 어떤 점에서, 그래서 나는 수레를 사용하여 추측하고


은 현대적인 하드웨어를 제외하고 (대신 복식을 사용)하지 않는 규칙입니다

은 이미 그렇게 할 수 있습니다. 그러나 문제는 작동하는 타입 변환 룰 세트가 생겨나 고 있습니다 ... 그리고 실제로 이해하기 위해서는 Java-ology 학위를 필요로하지 않을 정도로 간단합니다. 0.1을 갖는 것은 다른 상황에서 다른 것을 의미하는 것은 혼란 스러울 것입니다.그리고 이것을 고려하십시오 :

void method(float f) { ... } 
void method(double d) { ... } 

// Which overload is called in the following? 
this.method(1.0); 

프로그래밍 언어 디자인은 까다 롭습니다. 한 영역의 변화는 다른 영역에 영향을 미칠 수 있습니다.


UPDATE은 @supercat에 의해 제기 된 몇 가지 포인트를 해결합니다.

@ superpercat : 위의 오버로드를 감안할 때 어떤 메소드가 메소드 (16777217)에 대해 호출됩니까? 그게 최선의 선택입니까?

잘못 ... 컴파일 오류가 발생했습니다. 사실 대답은 method(float)입니다.

15.12.2.5. Choosing the Most Specific Method

하나 개 이상의 멤버 메소드가 메소드 호출에 접근 가능하고 적용 둘 경우,이에 대한 설명을 제공하기 위해 하나를 선택하는 것이 필요하다 :

JLS이 말한다 런타임 메쏘드 디스패치. Java 프로그래밍 언어는 가장 구체적인 방법이 선택되는 규칙을 사용합니다.

...

[기호의 M1 및 M2 적용 가능한 방법을 나타낸다.]

를 [] 만약 m2는 일반 및 M1과 M2 아니다이다 엄격한 또는 느슨하게하여 해당 호출이고 m1은 형식 매개 변수 유형 S1, ..., Sn을 가지고이고 m2는 형식 매개 변수 유형 T1, ..., Tn을 가지며 유형 si는 모든 i에 대해 인수 ei에 대해 Ti보다 구체적으로 입니다 (1 ≤ i ≤ n, n = k).

...

상기 조건은 하나의 방법 다른보다 구체적 일 수있는 상황 하에서 만하다.

S 유형 S는 S < : T (§4.10) 인 경우 임의의 표현식에 대해 유형 T보다 더 구체적입니다.

이 경우 전화에 모두 적용 가능한 method(float)method(double)을 비교합니다. float < : double부터 이 더 구체적이고이므로 method(float)이 선택됩니다.

@ superpercat : 이러한 동작으로 인해 문제가 발생할 수 있습니다.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이며 intdouble으로 변환되므로 결국 Math.Round(double)을 호출하게됩니다.

일반적으로 Java 산술은 "더 작은"숫자 유형에서 "더 큰"숫자 유형으로 암시 적으로 변환하지만 "큰"에서 "작은"로 변환하지 않습니다.

  • 정수의 제품을과 (말)을 float이 있기 때문에 부동 소수점 충분 정확하게 표현할 수 없습니다 :

    그러나, 당신은 여전히 ​​(당신의 반올림 예에서) 이후 신중해야합니까 int보다 정밀도가 낮습니다.

  • Math.round(double)의 결과를 정수 유형으로 형변환하면 정수 유형의 최소/최대 값으로 변환 될 수 있습니다.

그러나이 모든 것은 프로그래밍 언어의 산술 지원이 까다 롭고 새롭거나 부주의 한 프로그래머에게는 필연적 인 문제가 있음을 보여줍니다.

+0

정확도의 차이는 분명하지만 사용자가 선택에 대해 명시 적으로 요구하도록 결정한 것이 맞지만 그것이 얼마나 오랫동안 지속될 지 궁금합니다. float를 사용하는 것은 예외이며 예외는 아니며 대신 현대 하드웨어와 함께 규칙을 사용합니다. 그래서 어떤 시점에서 float f = 0.1을 쓸 때 사용자가 0.1f를 사용한다고 가정하는 것이 타당합니다. – arynaq

+0

위의 오버로드를 감안할 때 어떤 메소드가'method (16777217)'에 대해 호출 될 것입니까? 그게 최선의 선택입니까? – supercat

+0

@supercat - 컴파일 오류라고 생각합니다. "모호한 메서드 응용 프로그램"입니다. (시도해보고 ...) –

1

하, 이것은 내 친구 빙산의 일각에 불과합니다. 오른쪽

SomeReallyLongClassName x = new SomeReallyLongClassName(); 

예쁜 중복 : 다른 언어에서 오는

프로그래머 확실히에 비해 문자에 F 조금을 추가 할 필요 괜찮다?

더 많은 배경 지식을 얻으려면 핵심 Java 개발자에게 직접 이야기해야합니다. 그러나 순수한 표면 수준의 설명으로 이해해야 할 중요한 개념 중 하나는 표현입니다. Java에서 (나는 전문가가 아니므로 이것을 소금 한알로 생각하십시오.) 컴파일러 수준에서 코드가 표현식으로 분석됩니다. 그래서 :

float f 

는 형태를 가지며,

0.1f 

또한 타입 (float)을 갖는다.

일반적으로 한 표현식을 다른 표현식에 할당하려는 경우 유형이 일치해야합니다. 이 규칙이 완화 된 몇 가지 매우 구체적인 경우가 있습니다 (예 :, Integer과 같은 참조 유형의 int과 같은 프리미티브를 복싱하기; 그러나 일반적으로 그것은 붙 든다.

그것은이 경우 바보 보일 수도 있지만, 여기에 너무 바보 같지 않습니다 매우 유사한 사건 : 이제

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와 같습니다.

0

float의 정밀도가 매우 낮으므로 더 흥미로운 질문이 있습니다. 왜 그것은 전혀 지원됩니까? 드물기 때문에 float이 동일한 메모리를 사용할 수 있거나 (수백만 개가있는 경우) 또는 플로트가 필요한 데이터와 데이터를 교환해야하는 경우가 있습니다.

일반적으로 double을 사용하면 최신 PC에 비해 훨씬 빠르며 메모리 절약은 추가 정밀도에 비해 미미합니다.

Java는 값이 사용되는 방식을보기 위해 어떤 상황에서도 왼쪽을 보지 않습니다. 예 : 메서드의 반환 유형은 서명의 일부가 아닙니다. 경우에 따라 할당 및 운영자 할당을 위해 암묵적으로 캐스팅되지만 이는 대개 C와의 호환성을 유지하기위한 것이며 임시적인 IMHO입니다.