개요
C, Java 등을 배우다 보면 가장 처음에 배우는 게 자료형입니다
저도 고등학교 입학 전 겨울 방학 때 배웠을 때 이 수를 넘어가는 건 어떻게 쓸까라는 생각을 했었죠
그 궁금증을 해결해봅시다
하지만 해당 포스팅에서는 Go를 사용할 것입니다
오버플로우, 언더플로우
그 전에 오버플로우, 언더플로우를 알아야 합니다.
정리하면 자료형이 가진 값(메모리 할당량)을 넘어선 상태입니다.
오버플로우 (overflow) : 값이 해당 자료형이 가진 수보다 커진 상태로, 이상 값을 도출하는 상태
예시 ) 0 ~ 127까지 담을 수 있는 자료형에 128가 들어와서 값이 0이 되는 상태
언더플로우 (underflow) : 값이 해당 자료형이 가진 수보다 작아진 상태로, 이상 값을 도출하는 상태
예시 ) 0 ~ 127까지 담을 수 있는 자료형에 -1이 들어와서 값이 127이 되는 상태
※ 2000년대 초반 C표준이 개정되면서 둘 다 overflow라 불리며 underflow는 부동소수점과 stack에서만 사용한다는 글이 있지만 옳고 그름을 떠나 통상적인 커뮤니케이션에 맞추는 쪽이 더 좋다고 생각합니다, 그래도 알고 있는 게 좋죠 ※
해당 글 : https://80000coding.oopy.io/10d17093-d9cd-4edb-8d2b-55a4bff86565
Go에서의 Overflow
package main
import "fmt"
func main() {
// 가장 큰 자료형은 18446744073709551615 까지 가능
var number1 = uint64(18446744073709551615)
// 아래 변수는 에러 발생
var number2 = uint64(18446744073709551616)
fmt.Println(number1)
fmt.Println(number2)
}
결과물
# command-line-arguments
.\main.go:9:23: cannot convert 18446744073709551616 (untyped int constant) to type uint64
Go에서의 해결법
이번에 짤 코드는 두 변수를 문자열로 값을 입력받은 다음 두 개다 int slice로 바꾼 다음 더하고 나온 값을 문자열로 바꿔주기에 기존에 uint64의 메모리 할당량을 초과한 값도 받을수 있게 코딩할 것 입니다.
폴더 구조
main.go
package main
import (
"bignumber-cal.com/bignumber"
"fmt"
)
func main() {
// uint64가 아닌 문자열을 사용
var number1 = "18446744073709551"
var number2 = "18446744073709551616"
fmt.Println(bignumber.AddLargeNumbers(number1, number2))
}
bignumber/calculator.go
package bignumber
import (
"strconv"
)
// AddLargeNumbers : 엄청 큰 숫자도 더할 수 있게 해줌
func AddLargeNumbers(a, b string) string {
// 두 정수를 문자열에서 int 슬라이스로 변환
number1 := stringToIntSlice(a)
number2 := stringToIntSlice(b)
// 항상 number1이 더 큰 수로
if len(number1) < len(number2) {
number1, number2 = number2, number1
}
// 배열 차이만큼 빈 배열을 만들어서 number2에 앞에 추가하기
number2 = append(make([]int, len(number1)-len(number2)), number2...)
carry := 0
result := make([]int, len(number1))
// 뒤에서부터 계산
for i := len(number1) - 1; i >= 0; i-- {
sum := number1[i] + number2[i] + carry
result[i] = sum % 10
carry = sum / 10
}
// 결과를 문자열로 변환 + 만약 number1과 number2가 같은 자릿수로 carry 값이 생겼을 때 더하기
var resultString string
if carry > 0 {
resultString = strconv.Itoa(carry)
}
for _, digit := range result {
resultString += strconv.Itoa(digit)
}
return resultString
}
// stringToIntSlice : 문자열을 숫자 slice로 변경
func stringToIntSlice(s string) []int {
var result []int
for _, char := range s {
digit, _ := strconv.Atoi(string(char))
result = append(result, digit)
}
return result
}
결과물
// 출력
18465190817783261167
math/big 패키지
사실 패키지가 존재해서 구현할 필요는 없습니다, 이걸 사용하면 메모리 할당에 대한 제한이 없이 사용 가능합니다.
참고로 해당 big 패키지도 슬라이스로 해결했습니다
package main
import (
"fmt"
"math/big"
)
func main() {
var number1 = new(big.Int)
var number2 = new(big.Int)
var result = new(big.Int)
// 뒤에 base 파라티머에 경우는 10진수를 의미합니다.
number1.SetString("18446744073709551", 10)
number2.SetString("18446744073709551616", 10)
result.Add(number1, number2)
fmt.Println(result)
}
둘의 출력 값은 똑같습니다.
마무리
오타 및 피드백(개선방안 또는 다른 스타일의 코드)은 언제나 환영입니다.
감사합니다.
'Go Lang > Study' 카테고리의 다른 글
[GoLang] .env 파일에 DB 접속 정보를 저장하자 (0) | 2023.10.04 |
---|---|
[GoLang] xls 파일을 xlsx 파일로 바꿔주자 (0) | 2023.09.17 |
[GoLang] 정규식으로 URL 분석기 만들기 (0) | 2023.08.30 |
GoLang으로 느낌 있게 채팅 방 만들어보자 (0) | 2023.08.27 |
Go로 CRUD REST API 만들기 (3) - Bolt DB 연결 (0) | 2023.07.10 |