[java] Java에서 double을 사용하여 정밀도 유지

public class doublePrecision {
    public static void main(String[] args) {

        double total = 0;
        total += 5.6;
        total += 5.8;
        System.out.println(total);
    }
}

위 코드는 다음과 같이 인쇄됩니다.

11.399999999999

11.4를 인쇄 (또는 그대로)하려면 어떻게해야합니까?



답변

다른 사람들이 언급했듯이 BigDecimal11.4의 정확한 표현을 원한다면 클래스 를 사용하고 싶을 것입니다 .

이제 왜 이런 일이 일어나는지 약간의 설명이 있습니다.

Java 의 floatdouble기본 유형은 부동 소수점 숫자이며 숫자는 분수와 지수의 이진 표현으로 저장됩니다.

보다 구체적으로, double유형 과 같은 배정 밀도 부동 소수점 값 은 64 비트 값입니다.

  • 1 비트는 부호를 나타냅니다 (양수 또는 음수).
  • 지수에 대해 11 비트.
  • 유효 자릿수 (이진수로서 소수 부분)는 52 비트입니다.

이 부분들은 결합되어 double가치를 나타냅니다.

(출처 : Wikipedia : 배정 밀도 )

Java에서 부동 소수점 값을 처리하는 방법에 대한 자세한 설명은 4.2.3 섹션 : Java 언어 사양의 부동 소수점 유형, 형식 및 값 을 참조하십시오.

byte, char, int, long유형되는 고정 소수점 숫자의 정확한 representions있는 숫자. 고정 소수점 숫자와 달리 부동 소수점 숫자는 “대부분의 시간”으로 가정해도 숫자의 정확한 표현을 반환 할 수 없습니다. 이것이 당신 11.399999999999의 결과로 끝나는 이유 입니다 5.6 + 5.8.

1.5 또는 150.1005와 같이 정확한 값이 필요한 경우 고정 소수점 유형 중 하나를 사용하면 숫자를 정확하게 나타낼 수 있습니다.

이미 여러 번 언급했듯이 Java에는 BigDecimal매우 많은 수와 매우 작은 수를 처리 하는 클래스가 있습니다.

BigDecimal클래스 의 Java API 참조에서 :

불변의 임의 정밀도 부호있는 10 진수 BigDecimal은 임의의 정밀도 정수 비 스케일 값과 32 비트 정수 스케일로 구성됩니다. 0이거나 양수이면 배율은 소수점 오른쪽의 자릿수입니다. 음수 인 경우 스케일링되지 않은 숫자 값에 스케일 부정의 거듭 제곱에 10을 곱합니다. 따라서 BigDecimal로 표시되는 숫자의 값은 (unscaledValue × 10 ^ -scale)입니다.

부동 소수점 숫자 및 정밀도와 관련하여 스택 오버플로에 대한 많은 질문이 있습니다. 관심을 가질만한 관련 질문 목록은 다음과 같습니다.

부동 소수점 숫자에 대한 아주 세부적인 내용을 알고 싶다면 모든 컴퓨터 과학자가 부동 소수점 산술에 대해 알아야 할 사항을 살펴보십시오 .


답변

예를 들어, 이중 숫자를 입력 33.33333333333333하면 얻는 값이 실제로 가장 가까운 표현 가능한 배정 밀도 값입니다.

33.3333333333333285963817615993320941925048828125

이것을 100으로 나누면 다음과 같은 결과가 나타납니다.

0.333333333333333285963817615993320941925048828125

배정도 숫자로 표현할 수 없으므로 가장 가까운 표현 가능한 값으로 반올림됩니다.

0.3333333333333332593184650249895639717578887939453125

이 값을 인쇄하면 소수점 이하 17 자리로 다시 반올림 되어 다음을 제공합니다.

0.33333333333333326


답변

값을 분수로만 처리하려면 분자 및 분모 필드를 보유하는 분수 클래스를 만들 수 있습니다.

더하기, 빼기, 곱하기 및 나누기 및 toDouble 메서드에 대한 메서드를 작성하십시오. 이 방법으로 계산 중에 실수를 피할 수 있습니다.

편집 : 빠른 구현,

public class Fraction {

private int numerator;
private int denominator;

public Fraction(int n, int d){
    numerator = n;
    denominator = d;
}

public double toDouble(){
    return ((double)numerator)/((double)denominator);
}


public static Fraction add(Fraction a, Fraction b){
    if(a.denominator != b.denominator){
        double aTop = b.denominator * a.numerator;
        double bTop = a.denominator * b.numerator;
        return new Fraction(aTop + bTop, a.denominator * b.denominator);
    }
    else{
        return new Fraction(a.numerator + b.numerator, a.denominator);
    }
}

public static Fraction divide(Fraction a, Fraction b){
    return new Fraction(a.numerator * b.denominator, a.denominator * b.numerator);
}

public static Fraction multiply(Fraction a, Fraction b){
    return new Fraction(a.numerator * b.numerator, a.denominator * b.denominator);
}

public static Fraction subtract(Fraction a, Fraction b){
    if(a.denominator != b.denominator){
        double aTop = b.denominator * a.numerator;
        double bTop = a.denominator * b.numerator;
        return new Fraction(aTop-bTop, a.denominator*b.denominator);
    }
    else{
        return new Fraction(a.numerator - b.numerator, a.denominator);
    }
}

}


답변

제한된 정밀도 10 진수 산술을 사용하고 1/3을 처리하려는 경우 동일한 문제가 발생합니다. 0.333333333 * 3은 1.00000000이 아니라 0.999999999입니다.

불행히도 5.6, 5.8 및 11.4는 이진수로 반올림되지 않습니다. 따라서 0.3333이 정확히 1/3이 아닌 것처럼 플로트 표현은 정확하지 않습니다.

사용하는 모든 숫자가 되풀이되지 않는 소수이고 정확한 결과를 원하면 BigDecimal을 사용하십시오. 또는 다른 사람들이 말했듯이, 귀하의 가치가 모두 0.01 또는 0.001의 배수 또는 무언가라는 의미에서 돈과 같다면 모든 것에 10의 고정 제곱을 곱하고 int 또는 long을 사용하십시오 (더하기 및 빼기) 사소한 : 곱셈을 조심하십시오).

그러나 계산을 위해 이진에 만족하지만 약간 친숙한 형식으로 인쇄하려는 경우 java.util.Formatter또는을 시도하십시오 String.format. 형식 문자열에서 double의 전체 정밀도보다 작은 정밀도를 지정하십시오. 10 개의 유효 숫자 (예 : 11.399999999999)는 11.4이므로 이진 결과가 소수의 소수 자릿수가 필요한 값에 매우 가까운 경우 결과는 거의 정확하고 사람이 읽을 수 있습니다.

지정하는 정밀도는 숫자로 수행 한 수학의 양에 따라 다릅니다. 일반적으로할수록 더 많은 오류가 누적되지만 일부 알고리즘은 다른 알고리즘보다 훨씬 빠르게 누적됩니다 ( ‘불안정한’이라고 함). 반올림 오류와 관련하여 “안정적”과 반대). 당신이하고있는 모든 것이 약간의 값을 추가하는 것이라면, 소수점 이하 1 자리를 떨어 뜨리면 물건이 정렬 될 것입니다. 실험.


답변

정밀 수학이 정말로 필요한 경우 java의 java.math.BigDecimal 클래스를 사용하는 것이 좋습니다. 다음은 BigDecimal대한 Oracle / Sun의 좋은 기사입니다 . 누군가가 언급 한 바와 같이 당신이 1/3를 대표 할 수 없다 동안, 당신은 할 수 있습니다 당신이 결과가되고 싶어 정확히 어떻게 정확하게 결정하는 힘이있다. setScale ()은 당신의 친구입니다 .. 🙂

자, 지금 당장 너무 많은 시간을 보냈기 때문에 귀하의 질문과 관련된 코드 예제가 있습니다.

import java.math.BigDecimal;
/**
 * Created by a wonderful programmer known as:
 * Vincent Stoessel
 * xaymaca@gmail.com
 * on Mar 17, 2010 at  11:05:16 PM
 */
public class BigUp {

    public static void main(String[] args) {
        BigDecimal first, second, result ;
        first = new BigDecimal("33.33333333333333")  ;
        second = new BigDecimal("100") ;
        result = first.divide(second);
        System.out.println("result is " + result);
       //will print : result is 0.3333333333333333


    }
}

그리고 내가 좋아하는 새로운 언어 인 Groovy를 연결하기 위해 여기에 똑같은 예가 있습니다.

import java.math.BigDecimal

def  first =   new BigDecimal("33.33333333333333")
def second = new BigDecimal("100")


println "result is " + first/second   // will print: result is 0.33333333333333


답변

이것을 세 줄의 예로 만들었을 것입니다. 🙂

정확한 정밀도를 원하면 BigDecimal을 사용하십시오. 그렇지 않으면 원하는 정밀도에 10을 곱한 정수를 사용할 수 있습니다.


답변

다른 사람들이 지적했듯이, 소수는 10의 거듭 제곱을 기반으로하고 이진수는 2의 거듭 제곱을 기반으로하기 때문에 모든 소수 값을 이진수로 표현할 수있는 것은 아닙니다.

정밀도가 중요한 경우 BigDecimal을 사용하지만 친숙한 출력을 원할 경우 :

System.out.printf("%.2f\n", total);

당신에게 줄 것이다 :

11.40