[Java] 위도/경도 값에 BigDecimal or double?
위/경도 값을 저장할 때 BigDecimal을 사용하는 것을 본 적이 있다.
하지만 BigDecimal을 사용하는 것이 좋은걸까? double을 사용하는 건 좋지 않은 걸까?
BigDecimal과 double에 어떤 특징이 있는지, 위경도에 어느 자료형이 적합할지 한 번 살펴보자.
double의 부동소수점 문제
오늘날 컴퓨터는 대부분 IEEE 754 부동 소수점 방식을 사용하기 때문에 Java에서 double을 사용해 소수를 표현한다면 소수점 약 15자리부터 오차가 발생할 수 있다.
오차를 해결하려면?
Java에는 BigDecimal이라는 자료형으로 부동소수점 방식으로 인해 생기는 오차를 막을 수 있다.
https://docs.oracle.com/javase/8/docs/api/java/math/BigDecimal.html
BigDecimal은 어떻게 정확하게 소수를 표시할 수 있을까?
BigDecimal의 내부를 살펴보면 여러 상태 값들이 있는 것을 확인할 수 있다.
BigDecimal은 이러한 상태 값들을 활용하여 정수로 소수를 다루기 때문에 정확한 표현을 할 수 있는 것이다.
BigDecimal이 소수를 표현하기 위한 상태 값들은 다음과 같은 것들이 있다.
BigInteger intVal; // 정수부 값
int precision; // 정수부, 소수부의 길이를 합한 값
int scale; // 소수부의 길이
String stringCache; // 숫자를 String으로 변환한 값
long intCompact; // 소수점을 제외한 전체 수
BigDecimal은 valueOf를 통해 생성하자
만약 BigDecimal을 사용하게 된다면 생성자를 통한 객체 생성은 피하는 것이 좋다.
다음 코드를 살펴보자.
double number = 1.0;
BigDeciaml bigDecimal = new BigDecimal(number); // 0.100000000000000005551115123...
위 코드의 경우 double에서 이미 오차가 발생한 상황이다. 그렇기 때문에 BigDecimal에 저장된 값도 오차가 있는 값이 들어가게 된다.
하지만 다음과 같이 valueOf를 통해 생성하면 해결할 수 있다.
valueOf는 내부에서 문자열로 변환시키는 로직을 수행하기 때문에 문제가 되지 않는다.
double number = 1.0;
BigDecimal bigDecimal = BigDecimal.valueOf(number);
public static BigDecimal valueOf(double val) {
return new BigDecimal(Double.toString(val));
}
위 코드에서 알 수 있듯, 직접 문자열로 변환하면 생성자를 사용해서 생성해도 위 문제를 해결할 수 있다.
double number = 1.0;
BigDecimal bigDecimal = new BigDecimal(Double.toString(number));
BigDecimal의 단점
BigDecimal을 사용했을 때 단점은 무엇이 있을까
제일 먼저 사용하기 까다롭다는 점이 있다.
수를 생성할 때 항상 객체 생성을 해야하며, 연산을 할 때도 연산자가 아닌 메서드를 통해 수행해야 한다.
즉, 다음과 같은 로직은 수행할 수 없다.
BigDeciaml a = BigDecimal.valueOf(1.1);
BigDeciaml b = BigDecimal.valueOf(2.2);
BigDecimal result = a * b; // error
위 로직을 수행하려면 다음과 같이 변경해주어야 한다.
BigDeciaml a = BigDecimal.valueOf(1.1);
BigDeciaml b = BigDecimal.valueOf(2.2);
BigDecimal result = a.multiply(b);
또, db에서의 연산도 꺼려진다. BigDecimal과의 mapping을 지원해주지 않는 db가 많아 형변환을 거쳐야 하기에 double보다 다루기 힘들 것이다.
두번째로, 메모리 오버헤드다. BigDecimal은 위에서 말했듯이 수를 표현하기 위해 항상 객체 생성이 필요하다. 또 BigDecimal은 정밀한 숫자를 표현하기 위해 객체가 차지하는 메모리 공간이 적지 않다.
위 단점이 있더라도 만약 정밀함이 더 중요한 서비스라면(ex 돈과 관련된 기능) BigDecimal의 사용은 필수적일 수 있다.
위/경도에는 어떤 자료형을 쓸까
만약 개발 시 지도 api를 활용한 서비스를 준비한다면 위경도 값을 api를 통해 받을 것이다.
대부분의 지도 API(Google Map, 네이버 지도, 카카오맵 등)은 WGS84 위도/경도 좌표계를 사용한다.
그럼 api를 받을 때 다음과 같은 형식으로 받을 것이다.
{ latitude: 37.51243121154322, longitude: 127.03763421212431 }
여기서 소수점 오차를 얼마나 허용할 수 있을까?
위/경도에서 5자리까지는 약 1m, 6자리는 약 10cm, 7자리는 약 1cm의 차이가 있다.
만약 매우 정밀한 위치 표시를 요구하는게 아니라면 double을 사용해서 좌표를 표시해도 무리가 없어보인다.
마무리
만약 위치, 지도를 사용하는 서비스를 개발 중이라면 위/경도의 자료형을 한 번 고민을 해보는 것이 좋을 것 같다.
참고
https://ko.wikipedia.org/wiki/IEEE_754
https://f-lab.kr/insight/understanding-and-utilizing-bigdecimal
https://devs0n.tistory.com/102
https://programtip.tistory.com/2456
'Language > Java' 카테고리의 다른 글
[Java] System.out.println의 사용을 지양하자 (0) | 2024.06.21 |
---|---|
[Java] utility class는 무엇으로 구현하는 것이 좋을까? (1) | 2024.04.15 |
[Java] 제네릭(Generic) (0) | 2024.03.12 |
[Java] Mockito (0) | 2023.07.31 |
[Java] hashCode() (1) | 2023.07.02 |
댓글
이 글 공유하기
다른 글
-
[Java] System.out.println의 사용을 지양하자
[Java] System.out.println의 사용을 지양하자
2024.06.21 -
[Java] utility class는 무엇으로 구현하는 것이 좋을까?
[Java] utility class는 무엇으로 구현하는 것이 좋을까?
2024.04.15 -
[Java] 제네릭(Generic)
[Java] 제네릭(Generic)
2024.03.12 -
[Java] Mockito
[Java] Mockito
2023.07.31