답변:
다른 사람들이 언급했듯이 BigDecimal
11.4의 정확한 표현을 원한다면 클래스 를 사용하고 싶을 것입니다 .
이제 왜 이런 일이 일어나는지 약간의 설명이 있습니다.
Java 의 float
및 double
기본 유형은 부동 소수점 숫자이며 숫자는 분수와 지수의 이진 표현으로 저장됩니다.
보다 구체적으로, double
유형 과 같은 배정 밀도 부동 소수점 값 은 64 비트 값입니다.
이 부분들은 결합되어 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)입니다.
부동 소수점 숫자 및 정밀도와 관련하여 스택 오버플로에 대한 많은 질문이 있습니다. 관심을 가질만한 관련 질문 목록은 다음과 같습니다.
부동 소수점 숫자에 대한 아주 세부적인 내용을 알고 싶다면 모든 컴퓨터 과학자가 부동 소수점 산술에 대해 알아야 할 사항을 살펴보십시오 .
BigDecimal
훨씬 낮은 속도보다 double
가 정확도 15 소수점을 가지고 이중으로 필요하지 않습니다이 경우, 당신은 단지 반올림해야합니다.
예를 들어, 이중 숫자를 입력 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);
}
}
}
numerator
하고 s denominator
해야 int
합니까? 부동 소수점 정밀도를 원하는 이유는 무엇입니까?
제한된 정밀도 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
double 유형의 정밀도 제한에 맞서고 있습니다.
Java.Math에는 임의의 정밀 산술 기능이 있습니다.
7.3에는 바이너리로 유한 표현이 없기 때문에 할 수 없습니다. 가장 가까운 곳은 2054767329987789 / 2 ** 48 = 7.3 + 1 / 1407374883553280입니다.
자세한 설명 은 http://docs.python.org/tutorial/floatingpoint.html 을 참조하십시오 . (Python 웹 사이트에 있지만 Java와 C ++에는 동일한 "문제"가 있습니다.)
해결책은 문제가 무엇인지에 따라 다릅니다.
private void getRound() {
// this is very simple and interesting
double a = 5, b = 3, c;
c = a / b;
System.out.println(" round val is " + c);
// round val is : 1.6666666666666667
// if you want to only two precision point with double we
// can use formate option in String
// which takes 2 parameters one is formte specifier which
// shows dicimal places another double value
String s = String.format("%.2f", c);
double val = Double.parseDouble(s);
System.out.println(" val is :" + val);
// now out put will be : val is :1.67
}
java.math.BigDecimal 사용
이중은 내부적으로 이진 분수이므로 때로는 소수를 정확한 소수로 표현할 수 없습니다.
모든 것에 100을 곱하고 센트 단위로 저장하십시오.
부동 소수점 숫자는 주어진 부동 소수점 숫자에 대해 다음으로 높은 부동 소수점 숫자가 있다는 점에서 실수와 다릅니다. 정수와 같습니다. 1과 2 사이의 정수는 없습니다.
1/3을 부동 소수점으로 표현할 방법이 없습니다. 그 아래에 부유물이 있고 그 위에 부유물이 있으며 그 사이에 일정한 거리가 있습니다. 그리고 1/3은 그 공간에 있습니다.
Apfloat for Java는 임의의 정밀 부동 소수점 숫자와 함께 작동한다고 주장하지만 결코 사용하지는 않았습니다. 아마 볼만한 가치가 있습니다. http://www.apfloat.org/apfloat_java/
Java 부동 소수점 고정밀 라이브러리 전에 비슷한 질문을 받았습니다.
BigDecimal을 사용하십시오. ROUND_HALF_EVEN과 같이 반올림 규칙을 지정할 수도 있습니다. ROUND_HALF_EVEN과 같이 거리가 같은 경우 짝수 이웃으로 반올림하여 통계 오류를 최소화합니다 (예 : 1.5와 2.5 반올림 2).
짧은 대답 : 항상 BigDecimal을 사용 하고 이중 인수가 아닌 String 인수 와 함께 생성자를 사용하고 있는지 확인하십시오 .
예를 들어, 다음 코드는 원하는대로 11.4를 인쇄합니다.
public class doublePrecision {
public static void main(String[] args) {
BigDecimal total = new BigDecimal("0");
total = total.add(new BigDecimal("5.6"));
total = total.add(new BigDecimal("5.8"));
System.out.println(total);
}
}
이중 값을 사용하는 것 외에 다른 선택이 없으면 아래 코드를 사용할 수 있습니다.
public static double sumDouble(double value1, double value2) {
double sum = 0.0;
String value1Str = Double.toString(value1);
int decimalIndex = value1Str.indexOf(".");
int value1Precision = 0;
if (decimalIndex != -1) {
value1Precision = (value1Str.length() - 1) - decimalIndex;
}
String value2Str = Double.toString(value2);
decimalIndex = value2Str.indexOf(".");
int value2Precision = 0;
if (decimalIndex != -1) {
value2Precision = (value2Str.length() - 1) - decimalIndex;
}
int maxPrecision = value1Precision > value2Precision ? value1Precision : value2Precision;
sum = value1 + value2;
String s = String.format("%." + maxPrecision + "f", sum);
sum = Double.parseDouble(s);
return sum;
}
BigDecimal을 사용하여 애 퍼드를 낭비하지 마십시오. 99.99999 %의 경우 필요하지 않습니다. java double 유형은 대략적인 크기이지만 거의 모든 경우에 충분히 정확합니다. 유효 숫자 14 자리에 오류가 있음을 유의하십시오. 이것은 정말로 무시할 만하다!
좋은 출력을 얻으려면 :
System.out.printf("%.2f\n", total);