2008-11-05 3 views
7

현재 나는이 방법이 있습니다double에 소수점 이하 n 자리가 있는지 확인하는 방법?

static boolean checkDecimalPlaces(double d, int decimalPlaces){ 
    if (d==0) return true; 

    double multiplier = Math.pow(10, decimalPlaces); 
    double check = d * multiplier; 
    check = Math.round(check);  
    check = check/multiplier; 
    return (d==check);  
} 

을하지만이 방법은 제가 기본이 수에 기본 10 수학을 아마 때문에 checkDecmialPlaces(649632196443.4279, 4) 실패합니다.

어떻게이 검사를 올바르게 할 수 있습니까?

double 값의 문자열 표현을 얻은 다음 regexp를 사용하여이를 확인했습니다.하지만 이상하게 느껴졌습니다.

편집 : 모든 답변 주셔서 감사합니다. 이 정말 더블을받을 경우는 그 경우에 나는 다음을 구현 :

private static boolean checkDecimalPlaces(double d, int decimalPlaces) { 
    if (d == 0) return true; 

    final double epsilon = Math.pow(10.0, ((decimalPlaces + 1) * -1)); 

    double multiplier = Math.pow(10, decimalPlaces); 
    double check = d * multiplier; 
    long checkLong = (long) Math.abs(check); 
    check = checkLong/multiplier; 

    double e = Math.abs(d - check); 
    return e < epsilon; 
} 

가 나는 절단에 round을 변경했습니다. round에서 수행 된 계산은 부정확도를 너무 많이 증가시키는 것으로 보입니다. 적어도 실패한 테스트 케이스에서. 내가 확인하고 내가했던 BigDecimal를 사용해야하는 '진짜'문자열 입력을받을 수 있다면 당신의 일부로서
지적 :

BigDecimal decimal = new BigDecimal(value); 
BigDecimal checkDecimal = decimal.movePointRight(decimalPlaces); 
return checkDecimal.scale() == 0; 

내가 얻는 double 값은 아파치 POI의 API에서 제공하는 읽기는 파일을 능가합니다. 나는 몇 가지 검사를하고 API가 숫자 세포에 대한 double 값을 반환하지만 나는 즉시 doubleDecimalFormat에 있음을 포맷 할 때 나는 정확한 표현을 얻을 수 있다는 것을 발견 :

DecimalFormat decimalFormat = new DecimalFormat(); 
decimalFormat.setMaximumIntegerDigits(Integer.MAX_VALUE); 
// don't use grouping for numeric-type cells 
decimalFormat.setGroupingUsed(false); 
decimalFormat.setDecimalFormatSymbols(new DecimalFormatSymbols(Locale.US)); 
value = decimalFormat.format(numericValue); 

이 또한 값을 작동 할 수 없습니다 정확하게 이진 형식으로 표현됩니다.

+0

에 대한 값을 심문 "제대로"많은 여기에 의미하지 않는다. 임의의 십진수에는 정확한 이진 표현이 없습니다. 당신은 10 진수를 찾고 있습니다. 그래서 사람들은 10 진수의 이진 값을 좋아할 것입니다. 가까운 거리에 얼마나 가까이 있습니까? –

답변

6

테스트가 실패는 당신의 정확도에 도달했기 때문에 이진 부동 소수점 표현은 IEEE754 double precision으로 약 16 자리입니다. 649632196443.4279를 10000으로 곱하면 이진 표현이 잘 리므로 나중에 반올림 및 나눌 때 오류가 발생하여 함수 결과가 완전히 무효화됩니다.

자세한 내용은 http://en.wikipedia.org/wiki/Floating_point#Accuracy_problems

를 참조하십시오 더 좋은 방법은 n+1 소수점 이하 자릿수가 특정 임계 값 이하 여부를 확인하는 것입니다. d - round(d)epsilon (limit 참조)보다 작 으면 d의 십진수 표현에는 중요한 소수점이 없습니다. 마찬가지로 (d - round(d)) * 10^nepsilon보다 작 으면 d는 많아야 n을 가질 수 있습니다.

d이 원하는 소수 자릿수를 유지하기에 충분하지 않은 경우를 확인하려면 의 DoubleConverter을 사용하십시오.

+0

또한 DoubleConverter Jon이 그의 대답은 BigDecimal과 거의 같다. 나는 왜 그가 그의 대답을 제거했는지 궁금합니다. – Turismo

3

모든 부동 소수점 산술과 마찬가지로 평등을 검사하지 말고 오류 (ε)가 충분히 작아야합니다.

당신은 교체하는 경우 :

return (d==check); 

뭔가가 작동해야

return (Math.abs(d-check) <= 0.0000001); 

처럼. 분명히, 엡실론은 당신이 검사하고있는 십진법의 수와 비교하여 충분히 작게 선택되어야합니다.

+0

가장 큰 문제는 오버플로입니다. check = d * multiplier; 큰 숫자의 경우에는 작동하지 않습니다. – tvanfosson

+0

IEE 754 64 비트 복소수는 어쨌든 약 18 자리의 정밀도를 가지므로 1000을 곱하면 오버플로 (약 10^304)가 발생하는 숫자로 올라가면 표현에 0 소수가 있음을 확신 할 수 있습니다. – paxdiablo

1

double 유형은 2 진 부동 소수점 숫자입니다. 소수점 부동 소수점처럼 항상 상대방을 처리하는 데있어서 명백한 부정확성이 있습니다. 나는 당신이 원하는 방식으로 작동하도록 함수를 작성할 수 있다는 것을 모릅니다.

숫자의 원래 소스 (아마도 문자열 입력)로 돌아가서 중요한 경우 십진수로 표시해야 할 것입니다.

5

숫자의 오른쪽에 소수점 오른쪽에 n 개의 유효 숫자가있는 숫자를 나타내려면 BigDecimal을 사용하십시오.

변경 불가능한 임의 정밀도 부호 십진수.BigDecimal은 임의 정밀도 정수 의 배율없는 값인 과 32 비트 정수 배율로 구성됩니다. 0 또는 양수인 경우, 축 은 소수점 오른쪽 의 자릿수입니다. 음수 인 경우 숫자의 크기 조정되지 않은 값은 배율의 배율에 10을 곱한 입니다. 따라서 의 값은 BigDecimal로 표시되는 숫자입니다 (unscaledValue × 10 크기).

scale가 나는 이것이 일반적으로 정말 행할 것을 확실하지 않다 setScale(int)

0

를 통해 설정할 수 있습니다. 예를 들어, 1.0e-13의 소수 자리는 몇 자리입니까? 산술을 수행하는 동안 반올림 오류가 발생하여 실제로는 단지 0이 위장한 경우 어떻게됩니까? 의 경우, 0이 아닌 숫자가 처음 N 소수 자리에있는 경우 당신은 같은 것을 할 수있는 요구하는 반면 :

static boolean checkDecimalPlaces(double d, unsigned int decimalPlaces){ 
     // take advantage of truncation, may need to use BigInt here 
     // depending on your range 
     double d_abs = Math.abs(d); 
     unsigned long d_i = d_abs; 
     unsigned long e = (d_abs - d_i) * Math.pow(10, decimalPlaces); 
     return e > 0; 
    } 
+0

실제로 당신의 대답이 도움이되었지만, 중요한 숫자를 잃지 않기 전에자를 때'd '를 곱해야 할 때 결함이 있습니다. - 질문의 편집을 참조하십시오. – Turismo

1

BigDecimal로 전환 할 수 있다면 Ken G가 설명하는대로 사용해야합니다.

그렇지 않은 경우 다른 답변에서 언급 한 바와 같이 여러 가지 문제를 처리해야합니다. 나에게, 당신은 이진수 (double)를 다루고 그 수의 10 진수 표현에 대해 질문한다; 즉, 문자열에 대해 묻습니다. 네 직감이 맞는 것 같아.

0

내가이 문자열 변환을 더 생각하고 지수

public int calcBase10Exponet (Number increment) 
{ 
    //toSting of 0.0=0.0 
    //toSting of 1.0=1.0 
    //toSting of 10.0=10.0 
    //toSting of 100.0=100.0 
    //toSting of 1000.0=1000.0 
    //toSting of 10000.0=10000.0 
    //toSting of 100000.0=100000.0 
    //toSting of 1000000.0=1000000.0 
    //toSting of 1.0E7=1.0E7 
    //toSting of 1.0E8=1.0E8 
    //toSting of 1.0E9=1.0E9 
    //toSting of 1.0E10=1.0E10 
    //toSting of 1.0E11=1.0E11 
    //toSting of 0.1=0.1 
    //toSting of 0.01=0.01 
    //toSting of 0.0010=0.0010 <== need to trim off this extra zero 
    //toSting of 1.0E-4=1.0E-4 
    //toSting of 1.0E-5=1.0E-5 
    //toSting of 1.0E-6=1.0E-6 
    //toSting of 1.0E-7=1.0E-7 
    //toSting of 1.0E-8=1.0E-8 
    //toSting of 1.0E-9=1.0E-9 
    //toSting of 1.0E-10=1.0E-10 
    //toSting of 1.0E-11=1.0E-11 
    double dbl = increment.doubleValue(); 
    String str = Double.toString (dbl); 
// System.out.println ("NumberBoxDefaultPatternCalculator: toSting of " + dbl + "=" + str); 
    if (str.contains ("E")) 
    { 
    return Integer.parseInt (str.substring (str.indexOf ("E") + 1)); 
    } 
    if (str.endsWith (".0")) 
    { 
    return str.length() - 3; 
    } 
    while (str.endsWith ("0")) 
    { 
    str = str.substring (0, str.length() - 1); 
    } 
    return - (str.length() - str.indexOf (".") - 1); 
} 
관련 문제