공부했던 자료 정리하는 용도입니다.

재배포, 수정하지 마세요.

 

 

 

 

java.math.BigInteger 클래스

final int signum;	//부호. 1(양수), 0, -1(음수) 셋중에 하나
final int[] mag;	//값(magnitude)

정수형으로 표현할 수 있는 값을 넘은 수들을 다룰 때  BigInteger 을 사용한다. 내부적으로는  int 배열을 사용해서 값을 다루기 때문에  long 타입보다 훨씬 큰 값을 다룰 수 있지만 성능은 떨어진다.  String 처럼 불변(immutable)이고 다른 모든 정수형처럼 값을  2의보수 형태로 표현한다.  BigInteger 는 위의 코드에서 알 수 있듯이 부호를 따로 저장하고 배열에는 값 자체만 저장한다. 그래서  signum 의 값이 음수( -1 )인 경우, 2의 보수법에 맞게  mag 의 값을 변환해서 처리한다. 그래서 부호만 다른 두 값의  mag 는 같고  signum 은 다르다.

 

 

 

BigInteger의 생성

BigInteger val;
val = new BigInteger("1234567890");	//문자열로 생성
val = new BigInteger("FFFF", 16);	//n진수(radix)의 문자열로 생성
val = BigInteger.valueOf(1234567890L);	//숫자로 생성

 BigInteger 를 생성하는 방법은 위의 예시처럼 여러 가지가 있지만 일반형으로 다룰 수 없는 수를 다루는 것이기 때문에 문자열로 숫자를 표현하는 것이 일반적이다.

 

 

 

다른 타입으로 변환

메서드 설  명
String toString( ) 문자열로 변환
String toString(int radix) 지정된 진법( radix )의 문자열로 변환
byte[ ] toByteArray( )  byte 배열로 변환
int    intValue( )
long    longValue( )
float    floatValue( )
double    doubleValue( )
 Number 로부터 상속받은 기본형으로 변환하는 메서드
byte    byteValueExact( )
int    intValueExact( )
long    longValueExact( )
정수형으로 변환(변환한 결과가 변환한 타입의 범위에 속하지 않으면 예외( ArithmeticException )를 발생)

 

 

 

BigInteger의 연산

메서드 설  명
BigInteger add(BigInteger val) 덧셈(this + val)
BigInteger subtract(BigInteger val) 뺄셈(this val)
BigInteger multiply(BigInteger val) 곱셈(this * val)
BigInteger divide(BigInteger val) 나눗셈(this / val)
BigInteger remainder(BigInteger val) 나머지(this % val)

 BigInteger 에는 정수형에 사용할 수 있는 모든 연산자와 수학적인 계산을 쉽게 해주는 메서드들이 정의되어있다.  BigInteger 는 불변이기 때문에 반환 타입이  BigInteger 이라는건 새로운 인스턴스가 반환된다는 의미이다. Java API를 보면, 메서드마다 연산기호가 적혀있기 때문에, 각 메서드가 어떤 연산자를 구현한 것인지 알 수 있다.

 

 

 

비트 연산 메서드

메서드 설  명
int bitCount( ) 2진수로 표현했을 때, 1의 개수(음수는 0의 개수)를 반환
int bitLength( ) 2진수로 표현헀을 때, 값을 표현하는데 필요한 bit수
boolean testBit(int n) 우측에서 n + 1번째 비트가 1이면 true, 0이면 false
BigInteger setBit(int n) 우측에서 n + 1번째 비트를 1로 변경
BigInteger clearBit(int n) 우측에서 n + 1번째 비트를 0으로 변경
BigInteger flipBit(int n) 우측에서 n + 1번째 비트를 전환(1 → 0, 0 → 1)

 BigInteger 는 큰 숫자를 다루는 대신 성능이 떨어지므로, 성능을 향상시키기 위해 비트단위로 연산하는 메서드들을 제공한다.  and ,  or ,  xor ,  not 과 같이 비트 연산자를 구현한 메서드 등이 있다. 

 BigInteger 일 경우 정수가 짝수인지 확인할 때 제일 오른쪽 비트가  0 일 것이므로,  testBit(0) 으로 마지막 비트를 확인하는 것이 더 효율적이다.

 

 

package Example;

import java.math.BigInteger;

public class Example {
	public static void main(String[] args) {
		for(int i = 1 ; i < 100 ; i++) {
			System.out.printf("%d! = %s%n", i, calcFactorial(i));
		}
	}
	
	static String calcFactorial(int n) {
		return factorial(BigInteger.valueOf(n)).toString();
	}
	
	static BigInteger factorial(BigInteger n) {
		if(n.equals(BigInteger.ZERO))	// 0!일 경우
			return BigInteger.ONE;	// 1을 반환
		else	// return n * factorial(n - 1);
			return n.multiply(factorial(n.subtract(BigInteger.ONE)));
	}
}

 BigInteger 를 이용해서  1! ~ 99! 까지 출력하는 예제이다.  long 타입으로는  20! 까지밖에 계산할 수 없지만  BigInteger 를 이용하면 그 이상의 수도 계산할 수 있다.  BigInteger 의 최대값은  +-2   Integer.MAX_VALUE 제곱인데, 10진수로  10^60억 제곱이다.

 

 

 

 

 


java.math.BigDecimal 클래스

private final BigInteger intVal;	// 정수(unscaled value)
private final int scale;	//지수(scale)
private transient int precision;	//정밀도(precision) - 정수의 자리수

   double 타입으로 표현할 수 있는 값은 범위가 넓지만 정밀도가 최대 13자리밖에 되지 않고 실수형의 특성상 오차를 피할 수 없다는 단점이 있다.  BigDecimal 은 실수형과 달리 정수를 이용해서 실수를 표현한다. 실수의 오차는 10진 실수를 2진 실수로 정확히 변환할 수 없는 경우가 있기 때문에 발생하는 것이므로, 오차가 없는 2진 정수로 변환하여 다루는 것이다. 실수를 정수와 10의 제곱의 곱으로 표현한다.( 정수 x 10^(-scale) )

   scale   0 부터  Integer.MAX_VALUE 사이의 범위에 있는 값이다. 그리고  BigDecimal 은 정수를 저장하는데  BigInteger 를 사용한다.( BigDecimal   BigInteger 처럼 불변(immutable)이다.)

 

 

package Example;

import java.math.BigDecimal;

public class Example {
	public static void main(String[] args) {
		BigDecimal val = new BigDecimal("123.45");
		
		System.out.println("val = " + val);
		System.out.println("unscaledValue = " + val.unscaledValue());
		System.out.println("scale = " + val.scale());
		System.out.println("precision = " + val.precision());
	}
}

 

실행결과

예를 들어  123.45   12345 x 10^(-2) 로 표현할 수 있다. 이 값이  BigDecimal 에 저장되면  intVal(unscaledValue)   12345 가 되고 소수점 이하의 자리를 의미하는  scale 의 값은  2 가 된다. 정수 전체의 자리수인  precision 의 값은  5 가 된다.

 

 

 

Bigdecimal의 생성

BigDecimal val;
val = new BigDecimal("123.4567890");	//문자열로 생성
val = new BigDecimal(123.456);	//double타입의 리터럴로 생성
val = new BigDecimal(123456);	//int, long타입의 리터럴로 생성가능
val = BigDecimal.valueOf(123.456);	//생성자 대신 valueOf(double)사용가능
val = BigDecimal.valudOf(123456);	//생성자 대신 valueOf(int)사용가능

 BigDecimal 을 생성하는 방법은 위의 코드처럼 여러 가지 방법이 있는데, 일반형으로 다룰 수 없는 수들을 다루는 것이기 때문에 문자열로 숫자를 표현하는 것이 일반적이다.  double 타입의 값을 매개변수로 갖는 생성자를 사용하면 오차가 발생할 수 있어서 주의해야 한다. 

 

 

 

다른 타입으로 변환

메서드 설  명
String toPlainString( ) 무조건 다른 기호 없이 숫자로만 표현
String toString( ) 필요시 지수형태로 표현
int    intValue( )
long    longValue( )
float    floatValue( )
double    doubleValue( )
( Number 로부터 상속받은 기본형으로 변환
byte    byteValueExact( )
short    shortValueExact( )
int    intValueExact( )
long    longValueExact( )
BigInteger    toBigIntegerExact( )
정수형으로 변환
(변환한 결과가 변환한 타입의 범위에 속하지 않으면 예외 ArithmeticException 를 발생시킴)

 

 

package Example;

import java.math.BigDecimal;

public class Example {
	public static void main(String[] args) {
		BigDecimal val = new BigDecimal(1.0e-22);
		
		System.out.println("toPlainString() = " + val.toPlainString());
		System.out.println("toString() = " + val.toString());
	}
}

 

실행결과

 toPlainString( )   toString( ) 의 경우 대부분의 경우 반환 결과가 같지만,  BigDecimal 을 생성할 때  1.0e - 22 와 같은 지수 형태의 리터럴을 사용했을 때 다른 결과를 얻는 경우가 있어서 주의해야 한다.

 

 

 

BigDecimal의 연산

메서드 설  명
BigDecimal add(BigDecimal val) 덧셈(this + vall)
BigDecimal subtract(BigDecimal val) 뺄셈(this - vall)
BigDecimal multiply(BigDecimal val) 곱셈(this * vall)
BigDecimal divide(BigDecimal val) 나눗셈(this / vall)
BigDecimal remainder(BigDecimal val) 나머지(this % vall)

 BigDecimal   BigInteger 처럼 불변(immutable)이므로 반환 타입이  BigDecimal 인경우 새로운 인스턴스가 반환된다. Java API를 보면 메서드마다 연산기호가 적혀있기 때문에 각 메서드가 어떤 연산자를 구현한 것인지 알 수 있다. 

 

 

package Example;

import java.math.BigDecimal;

public class Example {
	public static void main(String[] args) {
		BigDecimal bd1 = new BigDecimal("123.456");
		BigDecimal bd2 = new BigDecimal("0.1");
		BigDecimal bd_add = bd1.add(bd2);
		BigDecimal bd_sub = bd1.subtract(bd2);
		BigDecimal bd_mul = bd1.multiply(bd2);
		BigDecimal bd_div = bd1.divide(bd2);
		
		System.out.print("name \tvalue\tscale\tprecision\n");
		System.out.print("bd1\t" + bd1.unscaledValue() + "\t" + bd1.scale() + "\t" + bd1.precision() + "\n");
		System.out.print("bd2\t" + bd2.unscaledValue() + "\t" + bd2.scale() + "\t" + bd2.precision() + "\n");
		
		System.out.println();
		
		System.out.print("bd_add\t" + bd_add.unscaledValue() + "\t" + bd_add.scale() + "\t" + bd_add.precision() + "\n");
		System.out.print("bd_sub\t" + bd_sub.unscaledValue() + "\t" + bd_sub.scale() + "\t" + bd_sub.precision() + "\n");
		System.out.print("bd_mul\t" + bd_mul.unscaledValue() + "\t" + bd_mul.scale() + "\t" + bd_mul.precision() + "\n");
		System.out.print("bd_div\t" + bd_div.unscaledValue() + "\t" + bd_div.scale() + "\t" + bd_div.precision() + "\n");
	}
}

 

실행결과

 BigDecimal 의 메서드를 이용해서 연산하면 연산 결과의 정수, 지수, 정밀도가 달라진다. 곱셈에서는 위의 결과와 같이 두 피연산자의  scale 을 더하고, 나눗셈에서는 뺀다. 덧셈과 뺄셈에서는 둘 중에서 자리수가 높은 쪽으로 맞추기 위해서 두  scale 중에서 큰 쪽이 결과가 된다. 

 

 

 

반올림 모드 - divide( )와 setScale( )

BigDecimal divide(BigDecimal divisor)
BigDecimal divide(BigDecimal divisor, int roundingMode)
BigDecimal divide(BigDecimal divisor, RoundingMode roundingMode)
BigDecimal divide(BigDecimal divisor, int scale, int roundingMode)
BigDecimal divide(BigDecimal divisor, int scale, RoundingMode roundingMode)
BigDecimal divide(BigDecimal divisor, MathContext mc)

나눗셈을 처리하기 위한 메서드는 위의 예시와 같이 많은 종류가 있다. 나눗셈의 결과를 어떻게 반올림( roundingMode )처리할 것인가와, 몇 번째 자리( scale )에서 반올림할 것인지 지정할 수 있다.  BigDecimal 이 아무리 오차 없이 실수를 저장한다 해도 나눗셈에서 발생하는 오차는 어쩔 수 없다. 

 

 

상수 설  명
CEILING 올림
FLOOR 내림
UP 양수일때는 올림, 음수일 때는 내림
DOWN  UP 과 반대로 양수일 때는 내림, 음수일 때는 올림
HALF_UP 반올림(5이상 올림, 5미만 버림)
HALF_EVEN 반올림(반올림 자리의 값이 짝수면  HALF_DOWN , 홀수면  HALF_UP )
HALF_DOWN 반올림(6이상 올림, 6미만 버림)
UNNECESSARY 나눗셈의 결과가 딱 떨어지는 수가 아니면,  ArithmeticException 발생

 roundingMode 는 반올림 처리방법에 대한 것으로  BigDecimal 에 정의된  ROUND_ 로 시작하는 상수들 중에 하나를 선택해서 사용하면 된다.  RoundingMode 는 이 상수들을 정의한 것이다. 가능하면 열거형인  RoundingMode 를 사용하는 것이 좋다.

+  divide( ) 로 나눗셈한 결과가 무한소수인 경우, 반올림 모드를 지정해 주지 않으면  ArithmeticException 이 발생한다.

 

 

 

java.math.MathContext

이 클래스는 반올림 모드와 정밀도(precision)를 하나로 묶어놓은 것일 뿐인 클래스이다.  divide( ) 에서는  scale 이 소수점 이하의 자리수를 의미하는데,  MathContext 에서는  precision 이 정수와 소수점 이하를 모두 포함한 자리수를 의미한다는 것을 주의해야 한다.

 

 

package Example;

import java.math.*;

public class Example {
	public static void main(String[] args) {
		BigDecimal bd1 = new BigDecimal("123.456");
		BigDecimal bd2 = new BigDecimal("1.0");
		
		System.out.println("divide() : " + bd1.divide(bd2, 2, RoundingMode.HALF_UP));
		System.out.println("MathContext : " + bd1.divide(bd2, new MathContext(2, RoundingMode.HALF_UP)));
	}
}

 

실행결과

그래서 위의 실행결과를 보면  scale   2 일때 나눗셈의 결과가 소수점 두 자리까지 출력되는데  MathContext 를 이용한 결과는  precision 을 가지고 반올림하므로  bd1   precision   123456 에서 세 번째 자리에서 반올림되어  presicion   12000 이 아닌  12 가 된다. 여기에  scale   2 가 반영되어  1.2E + 2 가 된 것이다. 

 

 

 

scale의 변경

BigDecimal setScale(int newScale)
BigDecimal setScale(int newScale, int roundingMode)
BigDecimal setScale(int newScale, RoundingMode mode)

 BigDecimal   10 으로 곱하거나 나누는 대신  scale 의 값을 변경해서 같은 결과를 얻을 수 있다.  setScale( ) 을 이용하면 된다.  setScale( )   scale 값을 줄이는 것은  10   n 제곱으로 나누는 것과 같으므로,  divide( ) 를 호출할 때처럼 오차가 발생할 수 있어 반올림 모드를 지정해 주어야 한다.

 

 

 

+ Recent posts