2012-03-12 8 views
1

가능한 중복 :
Double precision problems on .NET
Double calculation producing odd result예측할 수없는 이중

나는 double 값 0.2의 내부 represantation이 0.199999 같은 것을 알고있다. 그러나 다음 코드는 아직도 나를 혼란스럽게합니다.

CODE :

public static void main(String[] args) { 
    double d= 0.3d; 
    double f= 0.1d; 
    System.out.println(d+f); 
    System.out.println(d*f); 
    System.out.println(d); 
    System.out.println(f); 
    System.out.println(d-f); 
    System.out.println(d/f); 
    System.out.println((d-f)*(d-f)); 
} 

출력 : 실제로

0.4 
0.03 
0.3 
0.1 
0.19999999999999998 
2.9999999999999996 
0.039999999999999994 

을 무슨 일이야? 더하기, 곱셈은 잘되지만 뺄셈, 나누기는 잘되지 않습니다. 누구든지 더 자세히 설명해 주시겠습니까 추가가 뺄셈과 다른 이유는 무엇입니까?

+10

가장 많이 묻는 질문 중 하나이다. 컴퓨팅에 대한 질문에 대답했습니다. : P –

+1

... 그리고 on Stackoverflow – Thilo

+6

귀하의 질문에 첫 번째 문장은 대답을 것 같습니다! – assylias

답변

4

간단히 대답하면 표현 오류와 부동 소수점 연산에 대한 반올림 오류가 있다는 것입니다. toString()은 표현 오류에 대해 "인식"하므로 반올림 오류가없는 경우에는 표시되지 않습니다. 그러나 반올림 오류가 너무 크면 수행 할 수 있습니다.

해결책은 BigDecimal을 사용하거나 결과를 반올림하는 것입니다.


BigDecimal을 사용하는 경우 실제 값이 정확하게 표시됩니다.

double d = 0.3d; 
double f = 0.1d; 
System.out.println("d= " + new BigDecimal(d)); 
System.out.println("f= " + new BigDecimal(f)); 
System.out.println("d+f= " + new BigDecimal(d + f)); 
System.out.println("0.4= " + new BigDecimal(0.4)); 
System.out.println("d*f= " + new BigDecimal(d * f)); 
System.out.println("0.03= " + new BigDecimal(0.03)); 
System.out.println("d-f= " + new BigDecimal(d - f)); 
System.out.println("0.2= " + new BigDecimal(0.2)); 
System.out.println("d/f= " + new BigDecimal(d/f)); 
System.out.println("(d-f)*(d-f)= " + new BigDecimal((d - f) * (d - f))); 

인쇄

d= 0.299999999999999988897769753748434595763683319091796875 
f= 0.1000000000000000055511151231257827021181583404541015625 
d+f= 0.40000000000000002220446049250313080847263336181640625 
0.4= 0.40000000000000002220446049250313080847263336181640625 
d*f= 0.0299999999999999988897769753748434595763683319091796875 
0.03= 0.0299999999999999988897769753748434595763683319091796875 
d-f= 0.1999999999999999833466546306226518936455249786376953125 
0.2= 0.200000000000000011102230246251565404236316680908203125 
d/f= 2.999999999999999555910790149937383830547332763671875 
(d-f)*(d-f)= 0.03999999999999999389377336456163902767002582550048828125 

당신은 0.1 약간 너무 커서 0.3 약간 너무 작다는 것을 알 수 있습니다. 이것은 당신이 그들을 더하거나 증식 할 때 당신은 대략 옳은 숫자를 얻는다는 것을 의미합니다. 그러나 빼기 또는 나눗셈을 사용하면 오류가 누적되고 표현 된 수와 너무 먼 수를 얻습니다.

즉 0.1과 0.3은 0.4와 동일한 값을 갖지만 0.3 - 0.1은 0과 같은 값을 갖지 않습니다.BTW


2는

d-f= 0.20 
d/f= 3.00 
(d-f)*(d-f)= 0.04 

또는

System.out.println("d-f= " + roundTo6Places(d - f)); 
System.out.println("d/f= " + roundTo6Places(d/f)); 
System.out.println("(d-f)*(d-f)= " + roundTo6Places((d - f) * (d - f))); 

public static double roundTo6Places(double d) { 
    return (long)(d * 1e6 + (d > 0 ? 0.5 : -0.5))/1e6; 
} 

인쇄

System.out.printf("d-f= %.2f%n", d - f); 
System.out.printf("d/f= %.2f%n", d/f); 
System.out.printf("(d-f)*(d-f)= %.2f%n", (d - f) * (d - f)); 

인쇄를 사용할 수의 BigDecimal을 사용하지 않고 답을 반올림합니다

System.out.println("d-f= " + roundTo6Places(d - f)); 
System.out.println("d/f= " + roundTo6Places(d/f)); 
System.out.println("(d-f)*(d-f)= " + roundTo6Places((d - f) * (d - f))); 

라운딩 전에 0.1 후에 표현 될 수있는 값

double before_f = Double.longBitsToDouble(Double.doubleToLongBits(f) - 1); 
System.out.println("The value before 0.1 is " + new BigDecimal(before_f) + " error= " + BigDecimal.valueOf(0.1).subtract(new BigDecimal(before_f))); 
System.out.println("The value after 0.1 is " + new BigDecimal(f) + " error= " + new BigDecimal(f).subtract(BigDecimal.valueOf(0.1))); 
같이 계산 될 수


(의하여 toString 처리하도록 설계 만 표시 오차를 떠나) 라운딩 에러를 제거

인쇄

The value before 0.1 is 0.09999999999999999167332731531132594682276248931884765625 
    error= 8.32667268468867405317723751068115234375E-18 
The value after 0.1 is 0.1000000000000000055511151231257827021181583404541015625 
    error= 5.5511151231257827021181583404541015625E-18 
+0

고마워 :) 왜 0.1은 그 실제보다 더 많고 0.3 작은가? – Ahamed

+0

'0.1'은 이것이 가장 가까운 표현 가능한 값이기 때문에 조금 더 많습니다. '0.3'의 경우 가장 가까운 대표 값은 약간 적습니다. –

+0

OMG! 그 이유는 무엇입니까? (가장 가까운 점은 실제로 무엇을 의미합니까? – Ahamed

5

당신이 경우 정밀도를 위해 절망적이다. BigDecimal.

public static void main(String[] args) { 
    BigDecimal d = BigDecimal.valueOf(0.3d); 
    BigDecimal f = BigDecimal.valueOf(0.1d); 
    System.out.println(d.add(f)); 
    System.out.println(d.multiply(f)); 
    System.out.println(d); 
    System.out.println(f); 
    System.out.println(d.subtract(f)); 
    System.out.println(d.divide(f)); 
    System.out.println((d.subtract(f)).multiply(d.subtract(f))); 
} 

0.4 
0.03 
0.3 
0.1 
0.2 
3 
0.04 

아니면 결과 라운드

는, DecimalFormat는 꽤 잘에만 필요한 소수를 보여 의미 # 기호를 사용하여이 작업을 수행합니다 출력

double d = 0.3d; 
    double f = 0.1d; 
    DecimalFormat format = new DecimalFormat("#.##"); 
    System.out.println(format.format(d + f)); 
    System.out.println(format.format(d * f)); 
    System.out.println(format.format(d)); 
    System.out.println(format.format(f)); 
    System.out.println(format.format(d - f)); 
    System.out.println(format.format(d/f)); 
    System.out.println(format.format((d - f) * (d - f))); 

출력

0.4 
0.03 
0.3 
0.1 
0.2 
3 
0.04