2021. 5. 21. 00:06ㆍ개발/Golang
두번째 연산자 입니다. 이번에는 비교연산자를 알아보도록 하겠습니다.
1. 비교 연산자

- 비교 연산자는 if문, switch문, for문에 주로 사용합니다.
- 양변을 비교하여 만족하면 불리언값 true를, 만족하지 못할 경우 false를 반환하는 연산자입니다.
- 몇가지 주의 점이 있습니다. 부호가 있는 정수를 사용할 때 발생하는 오버플러와 언더플로 문제, 실수 끼리의 비교입니다.
1-1. 정수 오버플로 & 언더플로
package main
import "fmt"
func main() {
var x int8 = 127
fmt.Printf("%d < %d + 1: %v\n", x,x,x < x+1) // 비교연산자 수행
fmt.Printf("x\t = %4d, %08b\n", x,x)
fmt.Printf("x + 1\t = %4d, %08b\n", x+1,x+1)
fmt.Printf("x + 2\t = %4d, %08b\n", x+2,x+2)
fmt.Printf("x + 3\t = %4d, %08b\n", x+3,x+3)
var y int8 = -128
fmt.Printf("%d > %d - 1 : %v\n" , y,y,y-1)
fmt.Printf("y\t=%4d, %08b\n",y,y)
fmt.Printf("y-1\t =%4d,%08b\n",y-1,y-1)
}

- x는 int8인 127인 값입니다. 근데 결과 값이 false입니다. 그 이유는 int8의(최대값인 127에 1을 더한경우) 가장 큰값에서 가장 작은 값으로 변화하는게 오버플로입니다. 그래서 128이아니라 -128이 됩니다.
- y는 int8의 -128의 값이며, 가장 작은 값입니다. 이때 -1할경우 가장 큰값으로 바뀝니다. 이것이 언더플로입니다.
※ 정수 타입은 값의 경계에서 오버플로와 언더플로가 발생하기 때문에 연산에 주의해야합니다.
1-2. float 비교 연산
package main
import "fmt"
func main() {
var a float 64 = 0.1
var b float 64 = 0.2
var c float 64 = 0.3
fmt.Printf("%f + %f == %f : %v\n", a,b,c,a+b==c)
fmt.Println(a+b)
}

- 0.1+0.2 == 0.3을 수행했으나 값은 서로 같지 않습니다. 수행해보니 0.30000000000000004가 출력되었습니다. float64 표현 방식으로 생긴 오차이기때문입니다. float표현은 ==연산시 예기치 못한 오류가 발생할수 있습니다.
1-3. 실수 오차와 해결법
- 컴퓨터는 10진수가 아닌 2진수로 되어있어 정확히 표현하기 어려운 문제가 있습니다.
- float타입으로 최대한 가깝게 표현해도 오차가 발생할수 밖에 없습니다.
작은 오차 무시하기
package main
import "fmt"
const epsilon = 0.00001 // 매우 적은값
func equal(a,b float64) bool {
if a>b {
if a - b <= epsilon { // 작은 차이 무시
return true
} else {
return false
}
} else {
if b - a <= epsilon {
return true
}else{
return false
}
}
}
func main() {
var a float64 = 0.1
var b float64 = 0.2
var c float64 = 0.3
fmt.Printf("%0.18f + %0.18f = %0.18f\n",a,b,a+b)
fmt.Printf("%0.18f == %0.18f: %v\n",c,a+b,equal(a+b,c))
a = 0.0000000000004
b = 0.0000000000002
c = 0.0000000000007
fmt.Printf("%g == %g: %v\n",c,a+b,equal(a+b,c))
}

- 매우 작은 상수값 ( const )를 선언하고 epsilon(0.00001)이라고 했습니다. 이 값은 무시할 오차 한계를 정의한 값입니다.
- 소수점 18자리 까지 출력하면 0.1과 0.2가 정확하지 않다는걸 알수가 있습니다. 그래서 + 할경우 0.3아니라 오차가 발생합니다.
- c값도 0.3이지만 0.3이 아니고 2.99999... 인걸 알수 있습니다. 이 두값의 차이가 eqsilon보다 작기 때문에 두값을 같은 값으로 간주하고 true가 출력됬습니다.
작은오차를 없애는 더 나은 방법 : Nextafter()함수 이용하기
Go 언어에서는 math 패키지에서 Nextafter() 함수를 제공합니다.
이 함수를 이용해서 실숫값 대소비교를 할 수 있습니다.
package main
import (
"fmt"
"math" //수학연산을 위한 패키지
)
func equal(a,b float64) bool {
return math.Nextafter(a,b) == b // 값 비교
}
func main() {
var a float64 = 0.1
var b float64 = 0.2
var c float64 = 0.3
fmt.Printf("%0.18f + %0.18f = %0.18f\n" , a,b,a+b)
fmt.Printf("%0.18f == %0.18f: %v\n",c,a+b,equal(a+b,c))
a = 0.0000000000004
b = 0.0000000000002
c = 0.0000000000007
fmt.Printf("%g == %g: %v\n",c,a+b,equal(a+b,c))
}

- 두 실수를 비교하는 math패키지의 Nextafter() 함수를 이용하도록 변경하고 작은 비트값 만큼 오차범위 만 인정하게 되었습니다.
※ 오차를 무시하는 방법이지, 정확한 계산은 아닙니다. 금융프로그램이라면 math/big 패키지에서 제공하는 Float 객체를 이용해야합니다.
작은오차를 없애는 더 나은 방법 : Float 객체 이용하기
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(a,b,c,d)
fmt.Println(c.Cmp(d))
}

- math/big 패키지의 Float객체를 생성합니다.
- a = 0.1 ,b = 0.2, c = 0.3을 나타냅니다
- a+b의 값을 d에 저장하고 c와 d를 비교합니다 (Cmp)
- 비교시 c가 작을경우 -1을 c가 큰경우 1을 두값이 같은경우는 0을 출력합니다. 두값이 같기에 0을 출력합니다.
2. 논리 연산자
논리 연산자는 불리언 피연산자를 대상으로 연산해 결과로 true나 false를 반환합니다. &&, ||, ! 연산자를 제공합니다.
| && | AND | 양변이 모두 true이면 ture를 반환합니다. |
| || | OR | 양변 중 하나라도 true이면 true를 반환합니다. |
| ! | NOT | true이면 false를 반환하고 false이면 true를 반환합니다. |
3. 대입 연산자
대입 연산자는 우변의 값을 좌변(메모리 공간)에 복사합니다. 좌변은 반드시 저장할 공간이 있는 변수가 와야합니다.
var a int
a = 10 // 대입연산자
변수 a를 선언하고 대입 연산자를 통해서 a( 가리키는 메모리 공간 )에 10을 복사합니다.
[ 틀린 예 ]
var a int
var b int
a = b = 10 // 오류 발생 대입연산자는 결과를 반환하지 않습니다.
[ 올바른 예 ]
var a int
var b int
a = 10
a = b // 두줄로 나타내야지 됩니다.
복수 대입 연산자
우변의 개수와 좌변의 개수를 맞춰야합니다.
a , b = 3 , 4
첫째 3은 a 좌변주소에, 둘째 4는 b 좌변주소에 대입이 됩니다.
복합 대입 연산자
var a = 10
a = a + 2
변수 a를 선언하고 10으로 값을 초기화 합니다. 그리고 a+2를 대입했습니다 . a = a+2는 a+=2로 쓸수 있습니다. +=를 복합 대입 연산자라고 합니다.
모든 산술 연산자는 다 복합 대입 연산자로 쓸수 있습니다. 즉, +=, -=,*=,/=,%=,&=,|=,^=,<<=,>>= 등이 가능합니다.
증감 연산자
변수값 1을 증가하거나 1감소하는 구문은 자주 사용되어 증감문을 제공합니다. ++와 --을 두종류를 제공합니다.
var a int 10
// 아래의 세 구문은 모두 a값을 증가시킵니다.
a = a + 1 //a의 값을 1 증가시킵니다.
a += 1 //a의 값을 1 증가시킵니다.
a++ //a의 값을 1 증가시킵니다.
// 아래의 세 구문은 모두 a값을 감소시킵니다.
a = a - 1 //a의 값을 1 감소시킵니다.
a -= 1 //a의 값을 1 감소시킵니다.
a-- //a의 값을 1 감소시킵니다.
※ Go언어는 전위 증감연산자를 지원하지 않습니다. 즉 ++a는 사용하지못하고 a++만 가능합니다.
그 외 연산자
| 연산자 | 설명 |
| [] | 배열 요소에 접근할 때 사용합니다. |
| . | 구조체나 패키지 요소에 접근할 때 사용합니다. |
| & | 변수의 메모리 주솟값을 반환합니다. |
| * | 포인터 변수가 가리키는 메모리 주소에 접근합니다. |
| ... | 슬라이스 요소들에 접근하거나 가변 인수를 만들 때 사용합니다. |
| : | 배열의 일부분을 집어올때 사용합니다. |
| <- | 채널에서 값을 빼거나 넣을 때 사용합니다. |
※ 그 외의 연산자는 다른 강에서 배울 예정
연산자 우선순위
순위가 높은 연산자가 먼저 계산됩니다. 우선순위가 같으면 좌측부터 우측으로 연산됩니다.

'개발 > Golang' 카테고리의 다른 글
| 『Tucker의 Go 언어 프로그래밍』 스터디 요약 노트 : 8강 상수 (0) | 2021.05.23 |
|---|---|
| 『Tucker의 Go 언어 프로그래밍』 스터디 요약 노트 : 7강 함수 (0) | 2021.05.22 |
| 『Tucker의 Go 언어 프로그래밍』 스터디 요약 노트 : 6강 연산자 (1) (0) | 2021.05.16 |
| 『Tucker의 Go 언어 프로그래밍』 스터디 요약 노트 : 5강 fmt 패키지를 이용한 텍스트 입출력 (0) | 2021.05.16 |
| 『Tucker의 Go 언어 프로그래밍』 스터디 요약 노트 : 4강 변수 (0) | 2021.05.08 |