2014-12-19 2 views
6

Java에서 부동 소수점 산술과 그 정밀도에 관한 질문이 있습니다. 나는 여기와 구글을 통해 내 연구를 수행했지만 몇 가지 해결책을 찾아 왔지만 디자인에 어려움을 겪고있다. 그래서 자바에서는 정확한 계산을하기 위해 BigDecimal 클래스를 사용하고 있습니다. 변수는 double이고 값은 계산할 때 오른쪽으로 소수점 이하 8 자까지의 정확도를 가질 수 있습니다. 표시 할 결과 (정밀도)는 알려져 있으며 현재 값으로 저장할 것입니다. 또한 모든 값은 동적으로 (메소드를 통해) 들어옵니다. 전달 된 인수는 currentValue + 단계 크기 여야합니다.Java 부동 소수점 정밀도

public void newValue(float value) { 

    //Clip to valid range, can't go over minimum/max value 
    value = Math.max(minimumValue, Math.min(maximumValue, value)); 

    // TODO Implement better Floating Point Arithmetic precision 

    MathContext mcI = new MathContext(0, RoundingMode.HALF_UP);  
    MathContext mcF = new MathContext(8, RoundingMode.HALF_UP); 
    BigDecimal valueBD = new BigDecimal(value, mcF); 
    BigDecimal minimumBD = new BigDecimal(minimumValue, mcF); 
    BigDecimal stepBD = new BigDecimal(step, mcF); 
    BigDecimal currentValueBD = new BigDecimal(currentValue, mcF); 


    BigDecimal totalStepsBD = valueBD.subtract(minimumBD, mcF); 

    //Ensure value is divisible by stepsize 
    totalStepsBD = totalStepsBD.divide(stepBD, mcI); 

    valueBD = stepBD.multiply(totalStepsBD, mcF); 

    valueBD = valueBD.add(minimumBD, mcF); 

    // arithmetic without using BigDecimal (old) 
    //int totalSteps = (int) ((value- minimumValue)/ step); 
    //value = totalSteps * step + minimumValue; 

    if(!(valueBD.equals(currentValueBD))) { 
    valueBD = valueBD.setScale(displayPrecision, RoundingMode.HALF_UP); 
    currentValue = valueBD.floatValue(); 
    dispatch(); 
    } 

}

지금, 일부 값 전부는 아니지만 작동합니다. 특히 스텝 사이즈가 엉망이라면. 그래서 step = 0.1이라면 괜찮 았을 것입니다. 나는 그것을 0.005 한 경우, 나는 AirthmeticException를 얻을 것 - 비 종단 진수 확장을

totalStepsBD = totalStepsBD.divide(stepBD, mcI); 

이 0.005가 BigDeciaml (stepBD)를 한 후, 스텝 변수에 설정되어있는 단계가 나옵니다에. 0049999999 ... 그게 도움이 될지 모르지만 아이디어가 있다면 알려주세요. 고맙습니다.

+0

난 당신이 정의하는가 '어디 step' 볼 수 없습니다 사용, 그래서 있으리라 믿고있어이에 멤버 변수입니다 클래스. 'float'유형이고 0.005로 지정되면 .0049999999 또는 유사한 것으로 저장됩니다. 그런 다음'stepBD'에'step'의 값을 지정하면 0.005가 아닌 0.00499 값을 사용합니다. 이것이 문제인지 확실하지 않지만 도움이 될 수 있습니다. – Grice

+3

주 :'.equals()'를 사용하여'BigDecimal'을 비교하지 마십시오; 예를 들어 '0'과 '0.00'은 동일하지 않습니다. '.compareTo()'를 사용하고 대신'== 0'을 체크하십시오. – fge

답변

1

생성자에 String step (및 String value)을 전달하십시오. 0.005double (또는 float)으로 정확히 나타낼 수 없습니다.

BigDecimal stepBD = new BigDecimal("0.005"); // <-- works as a `String`. 

편집

또는 아래에 언급 한 바와 같이, BigDecimal.valueOf(double)

BigDecimal stepBD = BigDecimal.valueOf(0.005); 
+0

['BigDecimal.valueOf (double)'] (http://docs.oracle.com/javase/7/docs/api/java/math/BigDecimal.html#valueOf%28double%29)를 사용하는 것이 좋습니다.) 대신 OP가 자신의 코드를 그렇게 많이 변경하지 않아도됩니다. – Tom

+1

그냥 여기에 메모, BigDecimal.valueOf 나를 위해 작동하지 않았다. 오히려 첫 번째 해결 방법에서 보여준 것처럼 문자열로 캐스팅해야했습니다. 도와 주셔서 정말 감사합니다!! –

관련 문제