[Java] 소수점 반올림하는 3가지 방법

    Math.round 방식

    double a = 12.3456789;		
    System.out.println(a*100);
    1234.56789
    
    System.out.println((double)Math.round(a*100));
    1235.0
    
    System.out.println((double)Math.round(a*100)/100);
    12.35

    자바의 대표적인 수학모듈인 Math는 다양한 소수점을 처리하는 기능도 제공한다. Math.round를 쓰게 되면, 소수점 위치에 반올림하게 되는데 원하는 위치를 반올림을 하기 위해서는 별도의 인자값을 넣는 것이 아니라 위 소스처럼 그만큼의 소수점을 Shift해야 한다.

     

    즉 2자리까지 보여주고 반올림을 하고 싶을 경우 우선 현재 값에 100을 곱하고, Math.round를 쓰면 자리가 옮겨진 상태에서 소수점을 반올림하게 된다. 이때 다시 100으로 나눌 경우 소수점이 처리가 된다. 정말 번거롭기 그지 없는 방식이기 때문에 특별한 이유가 있지 않는 이상 나는 이 방식을 선호하지 않는다.

     

     

    String.format 방식

    double a = 12.3456789;		
    System.out.println(String.format("%.2f", a));
    12.35

    매우 직관적이고 쉬운 방식으로 필자가 자주 사용하는 방식이다. String.format을 한 후, 원하는 자리수를 지정하고 값을 대입하면 그만이다. 직관적이고 실수(Math.round같은 경우 곱하고 나눌 때 값을 잘못 쓴다든지...)를 할 가능성이 적기 때문에 특별한 문제가 없는 이상 이 방식을 사용하려 한다.

     

    NumberFormat 방식

    public static void main(String[] args) {
    	double a = 12.3456789;		
    	System.out.println(cutDecimal(2, a));
    }
     
     
     /**
     * 소수값을 적당한 길이로 잘라 리턴
     *
     * @param size   
     * @param value
     * @return
     */
     public static String cutDecimal(int cutSize, double value) {
     	NumberFormat nf = NumberFormat.getNumberInstance();
        nf.setMaximumFractionDigits(cutSize);
        nf.setGroupingUsed(false);
            
        return nf.format(value);
     }

    마지막으로 NumberFormat으로 소수점을 출력하는 방법이 있다. 그러나 보다시피 설정을 해야 하는 상당히 번거로운 작업이 있을 수 있다. 하지만 소수점을 처리를 자주 쓰게 된다면, 위와 같은 함수를 CommonUtil 등에 묶어서 해당 메소드를 호출하는 식의 처리를 한다면, 소스를 보다 깔끔하게 처리하고 유지보수 등으로 봤을 때 좋을 수 있다. 

     

    물론 NumberFormat 뿐만 아니라 나머지 2가지의 처리 방법도 메소드를 만들어서 동일한 방식으로 처리 해도 상관이 없을 것이다. 결과가 똑같이 나온다면 우리가 다음으로 알아야 봐야 할 것은 성능일 것이다.

     

     

    성능 테스트

    long startTime = System.currentTimeMillis();
    double a = 12.3456789;
    double dummy = 0.0;
    
    for(int i = 0; i < 100000; i++) {
    	cutDecimal(2, a);
    }
    long stop1 = System.currentTimeMillis();
    System.out.println((stop1-startTime) + "(ms)");
    
    for(int i = 0; i < 100000; i++) {
    	String.format("%.2f", a);
    }
    long stop2 = System.currentTimeMillis();
    System.out.println((stop2-stop1) + "(ms)");
    
    for(int i = 0; i < 100000; i++) {
    	dummy = (double)Math.round(a*100)/100;
    }
    long stop3 = System.currentTimeMillis();
    System.out.println((stop3-stop2) + "(ms)");

    위와 같이 NumberFormat, String.format, Math.round 방식 순으로 속도를 비교해보았다. 의미있는 속도를 위해서 총 10만번씩 연산을 수행해봤는데 

    348(ms)
    261(ms)
    1(ms)

    위와 같이 성능면에서 차이가 많이 난다는 것을 알 수 있다. 즉, Math.round 방식이 나머지보다 속도면에서 압도적으로 좋다는 것이다. 

     

    NumberFormat보다 String.format이 근소한 차이로 빨랐고 Math.round는 NumberFormat 방식보다 300배 정도 빠른 속도를 보여주었다. 이를 통해서 알 수 있는 것은 대용량의 처리(ex: 100만건 이상의 Batch 작업) 같은 경우 String.format이나 NumberFormat등을 쓰는 것보다 Math.round를 메소드화하여 만드는 것이 훨씬 좋을것이다.

     

     

    댓글

    Designed by JB FACTORY