본문으로 바로가기

정수 overflow

 

메모리에 할당된 크기 이상으로 값이 할당될 경우 값이 비정상적으로 변화하는 현상을 overflow라고 합니다.

package main

import "fmt"

func main() {
	var x int8 = 127; // 부호가 있는 정수 8비트. 따라서 최대값은 127임

	fmt.Println(x)
	fmt.Println(x + 1) // - 128이 되어버림
}

 

쉽게 말해, 8비트 만큼 메모리에 할당하였고, 최대값을 부여하였으므로

0111 1111 이 됩니다. 그런데 여기에 + 1을 하게되면, 1000 000이 됩니다.

부호가 있는 경우 맨 앞의 비트는 음수/양수를 구별하는데 사용되는 비트이므로, 값은 -128이 되어버립니다.

쉽게 말하자면, 메모리에 할당할 수 있는 값 중 가장 큰 값에 +1 을 하면 가장 작은 값으로 변하게 됩니다.

 

개념은 알겠는데 실제 프로덕트에서는 어떤 문제를 발생시킬 수 있을까요?

가장 쉬운 예로는, 돈 계산이 중요한 경우에 금액을 할당할 수 있는 메모리를 적게 설정하여 오버플로우가 되어 비교 연산이 잘못된 값을 반환하는 경우를 생각해볼 수 있겠습니다.

package main

import "fmt"

func main() {
	var x int8 = 127; // 부호가 있는 정수 8비트. 따라서 최대값은 127임

	fmt.Println(x < x + 1) // 수학적으로는 당연히 true여야 하는데... false 반환
}

 

정수 underflow

overflow와 반대로, 메모리의 크기에 따라 할당할 수 있는 가장 작은 값에 -1을 하면 최대값을 반환하는 현상을 underflow로 부릅니다.

package main

import "fmt"

func main() {
	var x int8 = -128; // 부호 있는 정수 8비트 가장 작은 값

	fmt.Println(x - 1) // 뺐는데 가장 큰 값인 127이 되어 버림
}

 

실수간 비교 연산

 

- 부동소수점 방식

 

우선 실수의 표현 방법(부동소수점)에 대해서 이해해야 실수간 비교 연산에서 이상한 오차가 생기는 이유를 이해할 수 있습니다.

실수는 소수부지수부로 나눠서 표현됩니다.

 

예를 들어서 314.1592 라는 값은

0.3141592 * 10^3으로 표현될 수 있습니다. 같은 표현으론 0.3141592e+03로 표현할 수도 있습니다.

여기서 소수부는 3141592이고, 지수부는 3입니다.

 

자, 이제 메모리에 대해 생각해봅시다.

float32 타입은 32비트(4바이트)가 메모리에 할당됩니다.

정수와 마찬가지로 첫번째 비트는 부호 비트, 뒤의 8비트는 지수부, 나머지 23비트는 소수부가 됩니다.

 

소수부에 할당할 수 있는 비트가 한계가 있다는 것은, 정확하게 표현할 수 있는 숫자에 한계가 있다는 말입니다.

float32는 7자리, float64는 15자리까지입니다. 따라서 아래와 같은 연산을 하면, 잘못된 값을 출력합니다.

package main

import "fmt"

func main() {
	var a float32 = 1234.1234
	var b float32 = 3456.123

	fmt.Println(a * b) // 원래 4265282.26758가 되어야 하는데 4.2652825e+06(426528.25)가 출력
}

 

 

- 실수 오차를 해결하기 위한 별도의 도구들

 

아래 코드는 직관과 다른 값을 출력합니다.

package main

import "fmt"

func main() {
	var a float64 = 0.1;
	var b float64 = 0.2;
	var c float64 = 0.3;

 	fmt.Println(a + b == c) // false ???
	fmt.Println(a + b) // 0.30000000000000004 ????
}

 

사실 이런 문제들 때문에, 정확한 값 계산, 비교를 위한 별도의 도구를 사용하곤합니다.

go에서는 math/Big 표준 패키지의 Float 객체를 사용해야 합니다.

js에서는 math.js, big.js 등 외부 패키지를 사용합니다. darrengwon.tistory.com/1354 여기 참고해보심이 ㅎ

이와 관련해서는, 사용하는 언어 별로 해결책들이 마련되어 있으니 구글링해서 해결해보심이..

 

아래는 math/Big을 사용하여 해결한 방법입니다.

package main

import (
	"fmt"
	"math/big"
)

func main() {
	a, _ := new(big.Float).SetString("0.1")
	b, _ := new(big.Float).SetString("0.2")
	c, _ := new(big.Float).SetString("0.3")

	d := new(big.Float).Add(a, b)

 	fmt.Println(c.Cmp(d)) // 같으면 0, c < d면 -1, c > d면 1
}

 

 


darren, dev blog
블로그 이미지 DarrenKwonDev 님의 블로그
VISITOR 오늘 / 전체