JAVA 부동소수점 계산 정밀도 손실 베이스 원리와 해결 방안
두 개의 더블 형식의 값을 연산하면 때때로 결과 값이 이상하는 문제가 발생할 수 있다.예:
System.out.println(19.99+20);
System.out.println(1.0-0.66);
System.out.println(0.033*100);
System.out.println(12.3/100);
출력:
39.989999999999995
0.33999999999999997
3.3000000000000003
0.12300000000000001
Java의 단순 부동소수점 유형인 float와 double는 정확하게 연산할 수 없습니다.이 문제는 사실 JAVA의 버그가 아니다. 컴퓨터 자체가 2진법이고 부동점수는 사실상 근사값이기 때문에 2진법에서 10진법 부동점수로 바뀔 때 정밀도를 잃어버리기 쉬워 정밀도가 떨어진다.정밀도 손실의 원리에 대해 간단하게 말할 수 있다. 우선 하나의 정수는 컴퓨터에서 01010 형식으로 표시하고 부동점수도 예외가 아니다.
예컨대 11, 11을 2로 나누면 5 여 1이다
5 나누기 2는 2 나머지 1
2를 2로 나누면 1이 되고 0이 된다.
1 을 2로 나누면 0 남짓 1이다
그래서 112진법은 1011이라고 한다.
더블 형식은 8바이트, 64위, 1위는 기호위, 뒤의 11위는 지수 부분, 나머지는 유효한 숫자를 차지한다.
정수를 2로 나누면 틀림없이 끝이 있을 것이다. 그 후에 2진법을 10진법으로 환원하려면 2를 곱하기만 하면 된다.
예를 들면: 0.99용 유효한 숫자 부분,
0.99 * 2 = 1+0.98 --> 1
0.98 * 2 = 1+0.96 --> 1
0.96 * 2 = 1+0.92 -->1
0.92 * 2 = 1+0.84 -->1
...............
이렇게 반복적으로 시작하는 것은 끝이 없고 더블 유효 숫자가 제한되어 있기 때문에 반드시 손실이 있기 때문에 2진법은 0.99를 정확하게 표시할 수 없다. 마치 10진법이 1/3을 정확하게 표시할 수 없는 것과 같다.
해결 방법:
에서 언급한 원칙은 플로트와 더블은 과학 계산이나 공학 계산으로만 사용할 수 있지만 상업 계산에서 우리는java를 사용해야 한다는 것이다.math.Big Decimal, Big Decimal 클래스를 사용하면 상술한 문제를 해결할 수 있다. 우선 주의해야 할 것은 문자열을 직접 사용하여 Big Decimal을 구성하는 것은 절대 정밀도 손실이 없다. 만약에 double 또는 double를string으로 바꾸면 Big Decimal을 구성하는 것은 정밀도 손실이 있기 때문에 이런 해결 방법은 사용 중에 부동점수를string으로 표시하여 저장하는 것이다. 연산을 직접string으로 double를 구성하는 것과 관련된다.그렇지 않으면 틀림없이 정밀도 손실이 있을 것이다.
1. 더하기
/**
*
* @param double1
* @param double2
* @return
*/
public static double add(String doubleValA, String doubleValB) {
BigDecimal a2 = new BigDecimal(doubleValA);
BigDecimal b2 = new BigDecimal(doubleValB);
return a2.add(b2).doubleValue();
}
2.상감
/**
*
* @param double1
* @param double2
* @return
*/
public static double sub(String doubleValA, String doubleValB) {
BigDecimal a2 = new BigDecimal(doubleValA);
BigDecimal b2 = new BigDecimal(doubleValB);
return a2.subtract(b2).doubleValue();
}
3. 곱하기
/**
*
* @param double1
* @param double2
* @return
*/
public static double mul(String doubleValA, String doubleValB) {
BigDecimal a2 = new BigDecimal(doubleValA);
BigDecimal b2 = new BigDecimal(doubleValB);
return a2.multiply(b2).doubleValue();
}
4. 상쇄
/**
*
* @param double1
* @param double2
* @param scale
* @return
*/
public static double div(String doubleValA, String doubleValB, int scale) {
BigDecimal a2 = new BigDecimal(doubleValA);
BigDecimal b2 = new BigDecimal(doubleValB);
return a2.divide(b2, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
}
5. 주 함수 호출
public static void main(String[] args) {
String doubleValA = "3.14159267";
String doubleValB = "2.358";
System.out.println("add:" + add(doubleValA, doubleValB));
System.out.println("sub:" + sub(doubleValA, doubleValB));
System.out.println("mul:" + mul(doubleValA, doubleValB));
System.out.println("div:" + div(doubleValA, doubleValB, 8));
}
결과는 다음과 같이 표시됩니다.
add:5.49959267
sub:0.78359267
mul:7.40787551586
div:1.33231241
그래서 가장 좋은 방법은 더블을 완전히 버리고string과java를 사용하는 것이다.math.BigDecimal.java는 IEE가 제정한 부동점수 표현법에 따라float,double 연산을 진행한다.이런 구조는 일종의 과학 계수법으로 기호, 지수와 끝수로 표시하고 밑수를 2로 정한다. 즉, 하나의 부동소수를 끝수로 표시하고 2의 지수를 곱한 다음에 기호를 추가한다.구체적인 밑바닥은 어떻게 저장하고 어떻게 운행하는지 제 블로그를 계속 지켜봐 주십시오. 다음에 상세한 상황을 정리할 것입니다.
이상은 본문의 전체 내용입니다. 본고의 내용이 여러분의 학습이나 업무에 일정한 도움을 줄 수 있는 동시에 저희를 많이 지지해 주시기 바랍니다!
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
38. Java의 Leetcode 솔루션텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.