큰 정수를 표현하기 위한 JavaAPI: BigInteger 클래스


[이미지 출처 - Google Search]
정수를 계산 할 때 정수의 크기가 byte, short, int, long과 같은 기본 데이터 타입의 범위를 초과하는 경우가 있습니다.

예를 들어, int 타입(32bit signed 2's complement representation)의 최소/최대 범위는
-2,147,483,648 ≤ Integer ≤ +2,147,483,647
로써, 팩토리얼을 계산할 경우 약 12! 까지는 올바른 결과를 구할 수 있으나 13! 부터는 overflow로 인해 정확한 결과를 얻을 수 없습니다.

BigInteger 클래스


BigInteger는 임의 자리의 정수를 표현하는 클래스로 String처럼 immutable 속성을 가지고 있습니다. 그리고 int 타입과 마찬가지로 사칙연산자를 메서드 형태로 제공하며, 유용한 수학적 메서드들도 함께 사용할 수 있습니다.

BigInteger 클래스의 몇 가지 특징을 팩토리얼 코드로 살펴보겠습니다.
public class BigIntegerTest {

    public static BigInteger factorial(int n) {
        //Point#1
        BigInteger result = BigInteger.ONE;

        for(int i=1; i<=n; i++)
            //Point#2
            result = result.multiply(BigInteger.valueOf(i));

        return result;
    }

    public static void main(String[] args) {
        System.out.println("fact(10) = "+ factorial(10));
        System.out.println("fact(12) = "+ factorial(12));
        System.out.println("fact(13) = "+ factorial(13));
        System.out.println("fact(999) = "+ factorial(999)); 
        System.exit(0);
    }
}
fact(10) = 3628800
fact(12) = 479001600
fact(13) = 6227020800
fact(999) = 402387260077093773543702433923003985719374864210714632543799910429938512398629020592044208486969404800479988610197196058631666872994808558901323829669944590997424504087073759918823627727188732519779505950995276120874975462497043601418278094646496291056393887437886487337119181045825783647849977012476632889835955735432513185323958463075557409114262417474349347553428646576611667797396668820291207379143853719588249808126867838374559731746136085379534524221586593201928090878297308431392844403281231558611036976801357304216168747609675871348312025478589320767169132448426236131412508780208000261683151027341827977704784635868170164365024153691398281264810213092761244896359928705114964975419909342221566832572080821333186116811553615836546984046708975602900950537616475847728421889679646244945160765353408198901385442487984959953319101723355556602139450399736280750137837615307127761926849034352625200015888535147331611702103968175921510907788019393178114194545257223865541461062892187960223838971476088506276862967146674697562911234082439208160153780889893964518263243671616762179168909779911903754031274622289988005195444414282012187361745992642956581746628302955570299024324153181617210465832036786906117260158783520751516284225540265170483304226143974286933061690897968482590125458327168226458066526769958652682272807075781391858178889652208164348344825993266043367660176999612831860788386150279465955131156552036093988180612138558600301435694527224206344631797460594682573103790084024432438465657245014402821885252470935190620929023136493273497565513958720559654228749774011413346962715422845862377387538230483865688976461927383814900140767310446640259899490222221765904339901886018566526485061799702356193897017860040811889729918311021171229845901641921068884387121855646124960798722908519296819372388642614839657382291123125024186649353143970137428531926649875337218940694281434118520158014123344828015051399694290153483077644569099073152433278288269864602789864321139083506217095002597389863554277196742822248757586765752344220207573630569498825087968928162753848863396909959826280956121450994871701244516461260379029309120889086942028510640182154399457156805941872748998094254742173582401063677404595741785160829230135358081840096996372524230560855903700624271243416909004153690105933983835777939410970027753472000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

1. 객체 생성


BigInteger 객체를 생성하는 방법은 크게 세 가지로 분류할 수 있습니다.
  • 미리 정의된 상수 객체
  • 생성자
  • 팩토리 메서드

팩토리얼 코드에서 result 참조변수를 초기화 할 때 사용한 ONE 객체가 미리 정의된 상수 중 하나로 정수 1에 해당하고, 그 외 0과 10에 해당하는 ZERO, TEN이 있습니다.
BigInteger zero = BigInteger.ZERO;  
BigInteger one = BigInteger.ONE;
BigInteger ten = BigInteger.TEN;

BigInteger 생성자는 여러 가지가 있으나, 두 가지만 살펴보겠습니다(그 외 API 문서 참조).
public static void constructorTest() {
    //생성자1. 문자열을 인자로 하는 경우
    BigInteger seven = new BigInteger("7");

    //생성자2. 바이트 배열을 인자로 하는 경우
    byte[] guess = {1, 0};
    BigInteger minusBigOne = new BigInteger(guess);

    System.out.println(minusBigOne);
}
BigInteger 클래스에는 정수(int)를 인자로 하는 생성자가 없습니다. 이유는 아마도 int 타입으로 표현할 수 있는 수의 크기가 제한적이기 때문일 것입니다. 대신 위 예처럼 문자열 형태의 수를 인자로 하는 생성자는 있습니다.

두 번째 생성자는 바이트 배열을 인자로 하는 생성자입니다. 출력 결과가 뭘까요?

정답은 256입니다. 생성자에 바이트 배열을 사용할 때 주의할 점은 바이트 배열의 원소 자체가 해당 위치의 비트값이 아니란 사실입니다. 즉 1바이트가 8비트이므로 {1, 0}은 [0000000100000000] = 28과 같습니다. [0000000000000010]으로 오해하지 않도록 주의하시기 바랍니다.


마지막 객체생성 방법은 팩토리 메서드 valueOf()를 이용하는 것입니다. 아래 코드는 모두 정수 10에 해당하는 BigInteger 객체를 생성하는 코드입니다.
BigInteger ten1 = BigInteger.TEN;

BigInteger ten2 = new BigInteger("10");

BigInteger ten3 = new BigInteger("10");

BigInteger ten4 = BigInteger.valueOf(10);

BigInteger ten5 = BigInteger.valueOf(10);
참조변수 ten1은 미리 정의된 상수필드를 그대로 가져왔고, ten2와 ten3는 문자열을 인자로하는 생성자를, 그리고 ten4와 ten5는 valueOf() 팩토리 메서드로 각각 객체를 생성했습니다. 각 방법이 어떤 차이가 있을까요?

ten1 ~ ten5 까지를 두 그룹으로 나눠보면 {ten1, ten4, ten5}와 {ten2, ten3}로 구분할 수 있습니다. 첫 번째 그룹의 특징은 모두 같은 객체를 가리킨다는 점이고, 두 번째 그룹은 같은 10을 표현하지만 서로 다른 객체를 가리킵니다. 즉, 첫 번째 그룹의 각 원소에 대한 동등비교 연산의 결과가 모두 "참"인 반면 두 번째 그룹은 "거짓"입니다.

요약하면, valueOf() 메서드는 캐쉬된 객체를 공유하는 방식으로 객체를 생성(참조)하는 특징이 있습니다.


2. Immutable


BigInteger 객체는 모두 String 객체처럼 immutable 객체입니다. 따라서 한번 생성된 객체의 상태를 변경할 수 없습니다. 예를 들면, 다음 코드는
public static void immutabilityTest() {
    BigInteger guess = new BigInteger("2");

    guess.add(new BigInteger("3"));

    System.out.println(guess);
}
guess 객체에 3을 더해 5가 출력될 것이라 예상하지만 결과는 2가 출력됩니다. 이것은 guess 객체의 상태를 2에서 5로 변경하는 것이 아니라 상태가 5인 새로운 BigInteger 객체를 생성한 결과입니다. 따라서 BigInteger 객체의 상태 갱신 코드는 다음과 같은 형태로 작성해야 합니다.
BigInteger result = BigInteger.valueOf(0);
for(int i=0; i<10; i++) {
    result = result.add(BigInteger.valueOf(i));
}
return result;


지금까지 Java의 BigInteger 클래스의 기본 사항에 대해 살펴보았습니다.


[참고문헌]
1. Java SE 7 API Document


0 댓글