자바 수치 범위 및 float 와 double 정밀도 손실 문제 해결

8299 단어 floatdouble정밀도
1.자바 중 int,float,long,double 수치 범위

public class TestOutOfBound { 
public static void main(String[] args) { 
 
System.out.println(Integer.MAX_VALUE-(-Integer.MAX_VALUE)); //     
System.out.println(Integer.MAX_VALUE); //2 31  -1,10   ,  20   ,          
System.out.println(Integer.MIN_VALUE); //  2 31   
System.out.println(Long.MAX_VALUE); //2 64  -1,19   ,   ,         
System.out.println(Long.MIN_VALUE); //  2 64   
System.out.println(Float.MAX_VALUE); //2 128  -1,38   , long    ,                  
System.out.println(Float.MIN_VALUE); //2 -149   
System.out.println(Double.MAX_VALUE); //2 1024  -1,308   , float   10 ,               
System.out.println(Double.MIN_VALUE); //2 -1074   
 
} 
} 
2.float 와 double 정밀도 손실 문제
예:

  :double result = 1.0 - 0.9; 
 
         ,    ,0.09999999999999998
왜 이 문제 가 발생 했 습 니까?이것 은 자바 와 다른 컴퓨터 언어 에서 모두 발생 할 수 있 는 문제 입 니 다.다음은 왜 이 문제 가 발생 했 는 지 분석 해 보 겠 습 니 다.
float 와 double 유형 은 주로 과학적 계산 과 공정 계산 을 위해 설계 되 었 다.그들 은 이 진 부동 소수점 연산 을 집행 하 는데 이것 은 광범 위 한 디지털 범위 에서 비교적 정확 하고 빠 른 유사 계산 을 제공 하기 위해 심혈 을 기울 여 설계 한 것 이다.그러나 이들 은 정확 한 결 과 를 제공 하지 않 았 기 때문에 우 리 는 정확 한 계산 장소 에 사용 해 서 는 안 된다.float 와 double 유형 은 특히 화폐 연산 에 적합 하지 않 습 니 다.float 나 double 이 0.1 또는 10 의 다른 마이너스 제곱 값 을 정확하게 표시 하 는 것 은 불가능 하기 때 문 입 니 다.마찬가지 로 이 진 시스템 도 1/10 을 정확하게 표시 할 수 없다.
부동 소수점 연산 은 매우 정확 하지 않 고 정밀도 가 표시 할 수 있 는 범 위 를 초과 하면 오차 가 발생 할 수 있다.흔히 오차 가 발생 하 는 것 은 수의 크기 때 문 이 아니 라 수의 정밀도 때문이다.따라서 결과 가 비슷 하지만 원 하 는 결 과 는 아니다.특히 float 와 double 을 사용 하여 정확 한 연산 을 할 때 특히 조심해 야 한다.
지금 우 리 는 부동 소수점 형 연산 이 왜 정밀도 손실 을 초래 하 는 지 상세 하 게 분석 해 보 자.

              : 
 
  (1)                
 
          。    ,11       : 
 
          11/2=5    1 
 
           5/2=2     1 
 
           2/2=1     0 
 
           1/2=0     1 
 
            0       11      (    ):1011 
 
         :           0     ,     ,       2           0。    ,                         ?    ,               ,        。 
 
  (2)                
 
          2         。    ,0.9        
 
          0.9*2=1.8        1 
 
          0.8(1.8     )*2=1.6        1 
 
          0.6*2=1.2        1 
 
          0.2*2=0.4        0 
 
          0.4*2=0.8        0 
 
          0.8*2=1.6       1 
 
          0.6*2=1.2        0 
 
              .........   0.9      (    ): 1100100100100...... 
 
       :          ,    *2           ,         。   ,                  。       ,              1/3 ?              1/10。                 "   "       。
3.해결 방법 1:
만약 에 자신 이 십 진법 의 소수점 을 기록 하 는 것 을 개의 치 않 고 수치 가 크 지 않다 면 롱,int 등 기본 유형 을 사용 할 수 있다.구체 적 으로 int 를 사용 하 느 냐 롱 을 사용 하 느 냐 와 관련 된 수치 범위 의 크기 를 봐 야 한다.단점 은 십 진법 소수점 을 스스로 처리 해 야 한 다 는 것 이다.가장 뚜렷 한 방법 은 화폐 사용 점 수 를 처리 하여 계산 하 는 것 이지 원(가감 만 관련)을 사용 하지 않 는 것 이다.
예:

int resultInt = 10 - 9;  
double result = (double) resultInt / 100;//             
4.해결 방법 2:
BigDecmal 을 사용 하고 구조 적 매개 변수 에 String 형식 을 사용 해 야 합 니 다.
'Effective Java'라 는 책 에서 해결책 을 제시 했다.이 책 에서 도 플 로 트 와 더 블 은 과학적 계산 이나 공정 계산 에 만 사용 할 수 있 고 상업 계산 등 정확 한 계산 에서 우 리 는 자바.math.BigDecimal 을 사용 해 야 한다 고 지적 했다.
BigDecimal 류 는 네 가지 방법 이 있 는데 우 리 는 부동 소수점 데 이 터 를 정확하게 계산 하 는 데 유용 한 방법 에 만 관심 을 가진다.즉,
BigDecimal(double value)//double 형 데 이 터 를 BigDecimal 형 데이터 로 변환 합 니 다.
사고방식 은 매우 간단 하 다.우 리 는 먼저 BigDecimal(double value)방법 을 통 해 double 형 데 이 터 를 BigDecimal 데이터 로 변환 한 다음 에 정상적으로 정확하게 계산 할 수 있다.계산 이 끝 난 후에 우 리 는 결 과 를 처리 할 수 있다.예 를 들 어 다 하지 못 한 결 과 를 반올림 할 수 있다.마지막 으로 결 과 를 BigDecimal 형 데이터 에서 double 형 데이터 로 변환 합 니 다.
이 생각 은 매우 정확 하지만 API 에서 BigDecimal 에 관 한 상세 한 설명 을 자세히 보면 정확 한 계산 이 필요 하 다 면 우 리 는 String 으로 BigDecimal 을 구성 하지 않 으 면 안 된다 는 것 을 알 게 될 것 입 니 다!그래서 우 리 는 BigDecimal 류 의 또 다른 방법,즉 정확 한 계산 을 할 수 있 는 BigDecimal(String value)방법 에 관심 을 가지 기 시작 했다.
//BigDecimal(String value)은 String 형 데 이 터 를 BigDecimal 형 데이터 로 변환 할 수 있 습 니 다.
그러면 문제 가 생 겼 습 니 다.상상 해 보 세 요.만약 에 우리 가 부동 소수점 데이터 의 덧셈 연산 을 하려 면 먼저 두 개의 부동 소수점 을 String 형 데이터 로 바 꾼 다음 에 BigDecimal(String value)로 BigDecimal 로 구성 한 다음 에 그 중 하나 에서 add 방법 을 호출 하여 다른 하 나 를 매개 변수 로 전달 한 다음 에 연산 결과(BigDecimal)를 부동 소수점 으로 바 꿔 야 합 니 다.만약 매번 부동 소수점 데이터 의 계산 을 할 때마다 이렇게 해 야 한다 면,당신 은 이렇게 번 거 로 운 과정 을 참 을 수 있 습 니까?적어도 난 못 해.그래서 가장 좋 은 방법 은 바로 종 류 를 써 서 이런 번 거 로 운 전환 과정 을 완성 하 는 것 이다.이렇게 해서 우리 가 부동 소수점 데이터 계산 을 해 야 할 때 이 종 류 를 호출 하면 된다.인터넷 상에 서 이미 고수 가 우리 에 게 이러한 전환 작업 을 완성 하기 위해 도구 류 Arith 를 제공 했다.이 는 다음 과 같은 정적 방법 을 제공 하여 부동 소수점 데이터 의 덧셈 과 곱셈 연산 을 완성 하고 그 결 과 를 반올림 하 는 작업 을 할 수 있 습 니 다.
public static double add(double v1,double v2)
public static double sub(double v1,double v2)
public static double mul(double v1,double v2)
public static double div(double v1,double v2)
public static double div(double v1,double v2,int scale)
public static double round(double v,int scale)
다음은 Arith 의 소스 코드 를 첨부 합 니 다.컴 파일 을 잘 저장 하고 부동 소수점 계산 을 하려 면 원본 프로그램 에서 Arith 류 를 가 져 오 면 상기 정적 방법 으로 부동 소수점 을 정확하게 계산 할 수 있 습 니 다.
부록:Arith 소스 코드

import java.math.BigDecimal; 
 
/** 
*   Java                   ,         
*        ,           。 
*/ 
 
public class Arith{ 
  //         
  private static final int DEF_DIV_SCALE = 10; 
  //         
  private Arith(){ 
  } 
 
  /** 
   *          。 
   * @param v1     
   * @param v2    
   * @return        
   */ 
  public static double add(double v1,double v2){ 
    BigDecimal b1 = new BigDecimal(Double.toString(v1)); 
    BigDecimal b2 = new BigDecimal(Double.toString(v2)); 
    return b1.add(b2).doubleValue(); 
  } 
  /** 
   *          。 
   * @param v1     
   * @param v2    
   * @return        
   */ 
  public static double sub(double v1,double v2){ 
    BigDecimal b1 = new BigDecimal(Double.toString(v1)); 
    BigDecimal b2 = new BigDecimal(Double.toString(v2)); 
    return b1.subtract(b2).doubleValue(); 
  } 
  /** 
   *          。 
   * @param v1     
   * @param v2    
   * @return        
   */ 
  public static double mul(double v1,double v2){ 
    BigDecimal b1 = new BigDecimal(Double.toString(v1)); 
    BigDecimal b2 = new BigDecimal(Double.toString(v2)); 
    return b1.multiply(b2).doubleValue(); 
  } 
 
  /** 
   *   (  )       ,          ,    
   *      10 ,         。 
   * @param v1     
   * @param v2    
   * @return        
   */ 
  public static double div(double v1,double v2){ 
    return div(v1,v2,DEF_DIV_SCALE); 
  } 
 
  /** 
   *   (  )       。          , scale    
   *    ,         。 
   * @param v1     
   * @param v2    
   * @param scale                 。 
   * @return        
   */ 
  public static double div(double v1,double v2,int scale){ 
    if(scale<0){ 
      throw new IllegalArgumentException( 
        "The scale must be a positive integer or zero"); 
    } 
    BigDecimal b1 = new BigDecimal(Double.toString(v1)); 
    BigDecimal b2 = new BigDecimal(Double.toString(v2)); 
    return b1.divide(b2,scale,BigDecimal.ROUND_HALF_UP).doubleValue(); 
  } 
 
  /** 
   *               。 
   * @param v           
   * @param scale          
   * @return          
   */ 
  public static double round(double v,int scale){ 
 
    if(scale<0){ 
      throw new IllegalArgumentException( 
        "The scale must be a positive integer or zero"); 
    } 
    BigDecimal b = new BigDecimal(Double.toString(v)); 
    BigDecimal one = new BigDecimal("1"); 
    return b.divide(one,scale,BigDecimal.ROUND_HALF_UP).doubleValue(); 
  } 
}; 
이상 자바 수치 범위 및 float 와 double 정밀도 손실 문 제 를 해결 하 는 것 은 바로 소 편 이 여러분 에 게 공유 하 는 모든 내용 입 니 다.참고 하 시기 바 랍 니 다.여러분 들 도 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.

좋은 웹페이지 즐겨찾기