🎲 컴퓨터가 정수를 표현하는 방법
Integers (정수)
소수점이 없는(분수 부분이 없는) 숫자를 의미 한다.
swift 의 Int는 크기에 따라 다양한 타입을 제공한다.
Int 가 컴퓨터에 어떤 형식으로 저장 되는지보자.
print("Int8 max: \(Int8.max)")
print("Int8 min: \(Int8.min)")
print("UInt8 max: \(UInt8.max)")
print("UInt8 min: \(UInt8.min)")
//Int8 max: 127
//Int8 min: -128
//UInt8 max: 255
//UInt8 min: 0
Int8 과 Uint8 의 최대값과 최소값을 찍어보았다.
우선 여기에서 뒤에 붙은 8은 비트의 수를 의미한다.
Int8 이라는 것은 8비트를 사용하는 Int 라는 것이다.
Int16 은 16비트 Int32는 32비트 Int64 는 64비트를 의미한다.
UInt 에서 앞에 있는 U 는 음의 정수를 포함하지 않는 정수 값을 의미한다.
그렇다면 Unit8 은 8비트 내에서 표현할 수 있는 양의 정수 타입 이라고 말할 수 있다.
그럼 UInt8 이 어떤식으로 저장되고 있을까?
2진수로 값이 저장되고 있기 때문에, 8개의 비트를 사용하여 최대 255까지 수를 나타낼 수 있는 것이다.
그렇다면 음의 정수 까지 나타낼 수 있는 Int8은 어떨까?
부호를 나타내야 하기 때문에 가장 처음의 비트를 부호를 나타내는 데에 사용한다.
(Sign bit 또는 MSB(Most Significant Bit) 부호를 나타내는 비트 라고도 한다.)
양수를 나타낼 때에는 부호를 나타내는 비트에 0이 들어가고 나머지는 일반 2진수로 나타내는 것과 방식이 똑같다.
음수를 나타낼때에는 2의 보수로 나타낸다.
그림을 보면 -4를 나타내고 있는데 실제 7개의 비트의 들어가 있는 숫자는 124 이다.
최대값이 128에서 4를 뺀 수이다.
-1 은 11111111 이렇게 -64 는 11000000 이런식으로 나타내게 된다.
이렇게 하는 이유는 연산을 할때 편하게 하기 위한 것이라고 한다.
비트 연산을 나중에 자세히 정리 해보자
정리해 보면
swift 는
Int8, Int16, Int32, Int64
UInt8, UInt16, UInt32, UInt64
비트의 형식으로 저장된다.
Int, UInt → 현재 운영 체제에 따라 비트 수 결정됨 (32, 64)
음수를 굳이 사용하지 않더라도 특별한 상황이 아니면 UInt 보다 Int 를 사용하는 것을 더 권장 한다.
🧮 컴퓨터가 실수를 표현하는 방법
🌱 이진수 실수 표현 원리
부동 소수점 타입 Float Double 을 이야기 하기 전에 컴퓨터가 실수를 어떻게 나타내는지를 먼저 살펴보자.
우선 이진수로 정수를 표현하는 것보다 실수를 표현하는 것은 더 까다롭다 🥲
우선 우리가 알고 있는 0.1 은 십진수를 기반으로 하면 1/10로 1을 10개로 나눈 수이다.
그림으로 보면 이렇다.
한 자리수 더 들어간 0.01 은 1/100 으로 나타낼 수 있다.
그런데 이진수에서 0.1 은 십진수에서는 0.5쯤에 해당된다.
0.1 + 0.1 = 1.0 이 되어야 하기 때문이다.
그럼 0.625 를 이진수로 나타낸다고 해보자.
이렇게 나타낼 수 있다.
🤔 부정확한 값
10진수 표기법도 2진수 표기법도 실수를 완전하게 표기하기엔 완벽하진 않다.
예를들어 1/3 을 10진수 로 표현 한다고 하자.
우리는 이미 결과를 알고 있다. 0.3333333... 이 되면서 무한 소수가 될 것이다.
이유는 3을 10의 제곱으로 표현할 수 가 없기 때문이다.
그럼 2진수의 경우는 어떨까?
0.1 을 2진수로 표현해 보자
1/10의 10을 2의 제곱으로 표현할 수가 없어서 무한 소수가 되게 된다.
두 경우다 정확한 수를 알기가 힘들다. 컴퓨터는 그냥 어느 지점에서 값을 잘라서 반올림한 근사치의 값을 가지게 된다.
이제 소수를 어떻게 2진법으로 표현하는지 알았으니 다양한 표현법에 대해서 알아보자.
📌 고정 소수점 방식 fixed point
부호표현 비트
정수 부분
소수 부분
을 나누어서 표현하는 방식 이다.
10.8 을 고정 소수점 방식에 따라서 나타낸다면,
10 -> 1010
0.8 -> 1100110011
이렇게 그림과 같이 나타낼 수 있다.
고정 소수점 방식은 잘 사용하지 않는다.
왜냐하면 사용하는 비트의 수의 비해 저장할 수 있는 수가 너무 적기 때문이다.
☁️ 부동 소수점 방식
전기 전자 기술자 협회 (IEEE)에서 개발한
부호표현 비트
지수 부분
가수 부분
을 나누어서 표현하는 방식이다.
자료형 | 크기 | 부호 | 지수 | 가수 |
---|---|---|---|---|
float | 32bit | 1bit | 8bit | 23bit |
double | 64bit | 1bit | 11bit | 52bit |
우선 가수 부분이란, 아까 고정 소수점 방식으로 2진수를 만든뒤, 가장 첫번째 1 전까지 소수점을 이동한 수를 말한다. (1은 당연하기 때문에 생략한다.)
하나씩 앞으로 이동할 때마다 값이 2의 제곱 씩 커지기 때문에 소수점을 얼마나 옮겼는지를 기억해 두어야 한다.
지수부분은 소수점을 옮긴 횟수에서 127을 더한 값을 의미한다.
127은 float 의 지수 표현 크기(8bit)의 중간값을 의미한다. 음수를 표현하는 비트를 가지지 않고 음수를 표현하기 위해서 사용한다.
그림으로 보면 이렇다.
그럼 위에서 고정 소수점으로 나타낸 10.8을 부동 소수점으로 나타내면
이렇게 된다.
double 부분도 원리는 똑같고 저장할 수 있는 크기만 다른 것이다.
가수 부분을 만들 때, 소수점을 이동하기 때문에 부동(떠다니는) 소수점 방식이라는 이름을 가지게 된 것 같다.
☁️swift 의 부동 소수점 타입
일반적으로 지원하는 Float(32bit), Double(64bit) 을 swift 에서도 지원한다.
Double은 최소 15자리의 소수점 정확도를 가지고 있고
Float 는 더 적은 6자리의 정확도를 가지고 있다.
여기서 말하는 정확도는, 위의 이야기한 부동 소수점이 저장할 수 있는 비트를 넘어가면 값을 생략하기 때문에 정확한 수가 아니라 근사치가 저장되기 때문이다.
♻️ 숫자 타입의 변환
정수 -> 부동소수
부동소수 -> 정수
이렇게 변환을 할때는 명시적으로(타입을 지정해주어서) 값을 지정해 주어야 한다.
공식문서의 예제를 보자
let three = 3
let pointOneFourOneFiveNine = 0.14159
let pi = Double(three) + pointOneFourOneFiveNine
// pi equals 3.14159, and is inferred to be of type Double
부동소수 -> 정수 로 변환될 때는 소수 부분은 버림 합니다.
let integerPi = Int(pi)
// integerPi equals 3, and is inferred to be of type Int
🤦🏻 부동 소수점 오류가 생기는 이유
위의 글을 다 읽었으면 이유를 추측해 볼 수 있다.
부동소수점 타입은 소수의 근사치를 저장한다.
값이 정확하지 않아서 연산을 시키면 미세하게 오류가 나게 되는 것이다.
🔟 Decimal
그래서 오류가 덜 나게 하려면 어떻게 해야 할까?
Decimal 이라는 타입을 사용하면 된다.
let decimalNumber : Decimal = Decimal(string: "0.17895")
이름과 같이 십진법으로 나타내게 된다.
(swift 가 정확히 어떤 원리로 dicimal 타입을 저장하는 지는 확실하지 않지만, 다른 언어에서는 배열에 각 자리수 숫자를 저장하는 방식을 사용한다고 주워 들었다. 정확하진 않다.)
아시는 분 있으면 댓글로 제발 알려주세요!
그런데, 이 방법이 또 완전히 정확하다 말하기 애매한 것은 아까같이 3/1 처럼 10진수로 나타내었을 때 무한 소수가 나오는 애들은 여전히 정확하지 않기 때문이다.
약간 씩의 연산 오류가 생길 수는 있다.
생각보다 컴퓨터는 바보다. 연산을 하라고 컴퓨터를 만들었는데, 연산도 제대로 못한다.
참고자료
basic 애플 공식 문서
Advanced Operators 애플 공식 문서
decimal 에 대한 포럼
'🍎 iOS > 🕊️ swift' 카테고리의 다른 글
[swift]@property wrapper (0) | 2023.01.06 |
---|---|
[swift] 의존성 주입으로 코드를 예쁘게 하자! (0) | 2023.01.06 |
[swift] 고차 함수 요목조목 보기 (0) | 2023.01.06 |
[iOS] Number Fomatter (0) | 2023.01.06 |
[swift] Subscript 구현하기 (0) | 2023.01.06 |