Programming language/Golang

[Go] 06 연산자

chaenii 2022. 1. 10. 23:46

본 게시물은 Tucker의 Go언어 프로그래밍을 참고해 작성한 게시물입니다.

http://www.yes24.com/Product/Goods/99108736

 

Tucker의 Go 언어 프로그래밍 - YES24

게임 회사 서버 전문가가 알려주는 Go 언어를 내 것으로 만드는 비법구글이 개발한 Go는 고성능 비동기 프로그래밍에 유용한 언어이다. 『Tucker의 Go 언어 프로그래밍』은 Go 언어로 ‘나만의 프로

www.yes24.com

 

6.1 산술 연산자

Go언어에서 모든 연산자의 각 항의 타입은 항상 같아야 한다.(시프트 연산은 예외) 

정수 타입과 실수 타입을 서로 더하거나 뺄 수 없다. 그래서 타입 변환을 통해서 타입을 같도록 맞춰준 다음에 연산해야 한다.

연산의 결과 타입도 인수 타입과 같다.

 

비트 연산자

  • &(AND 연산자)
  • |(OR 연산자)
  • ^ (XOR 연산자)
  • &^(비트 클리어 연산자)
    • 우변값에 해당하는 비트를 클리어하는 연산자이다.
      10 & ^2
      1단계 : ^2 : 0000 0010 => 1111 1101
      2단계 : 10 & ^2 : 0000 1010 & 1111 1101 = 0000 1000 
      우변에 해당하는 값의 1인 비트만 0으로 바뀐다.

 

시프트 연산자

  • << 왼쪽 시프트
    • n칸이동 : 원래 숫자의 2의 n승배가 된다. 
    • [예외]
      0111 1111 : 2비트만큼 왼쪽으로 이동
      1111 1110 : 오버플로우!
    • 시프트 연산을 할 때 값의 범위를 벗어나지 않는지 항상 주의해야 한다.
  • >> 오른쪽 시프트 
    • 부호가 있는 정수이면 왼쪽 비트에 부호와 같은 값으로, 부호가 없는 정수이면, 0으로 채워진다.
    • 음수이면 최상위 비트가 1이므로 1로, 양수이면 0으로 채워진다.
    •  
    • var x int8 = 16 // 0001 0000 var y int8 = -128 // 1000 0000 var z int8 = -1 // 1111 1111 var w uint8 = 128 // 1111 1111 fmt.Printf("x:%08b x>>2: %08b x>>2: %d\n", x, x>>2, x>>2) // 0000 0100 : 4 fmt.Printf("y:%08b y>>2: %08b y>>2: %d\n", y, y>>2, y>>2) // 1110 0000 : -32 fmt.Printf("z:%08b z>>2: %08b z>>2: %d\n", z, z>>2, z>>2) // 1111 1111 : -1 fmt.Printf("w:%08b w>>2: %08b w>>2: %d\n", w, w>>2, w>>2) // 0011 1111 : 32

 

6.2 비교 연산자 (==, !=, <, >, <=, >=) 

양변을 비교해 조건에 만족하면 불리언값 true, 만족하지 못할 경우 false를 반환하는 연산자

비교 연산자를 사용할 때 주의할 점

부호가 있는 정수를 사용할 때 발생하는 오버플로, 언더플로 문제, 실수끼리의 비교이다.

  • 정수 오버플로
  • 정수 언더플로
  • 실수끼리 비교

 

정수 언더플로

오버플로: 정수가 정수타입의 범위를 벗어난 경우 값이 비정상으로 변화하는 현상

오버플로로 인해 x < x + 1을 항상 만족하지 못할 수도 있다.

	var x int8 = 127 // 01111111

	fmt.Printf("%d < %d + 1: %v\n", x, x+1, x < x+1) // false
	fmt.Printf("x\t = %4d,  %08b\n", x, x)
	fmt.Printf("x+1\t = %4d,  %08b\n", x+1, x+1) // -10000000 => -128
	fmt.Printf("x+2\t = %4d,  %08b\n", x+2, x+2) // -10000000 + 1 => -127
	fmt.Printf("x+3\t = %4d,  %08b\n", x+3, x+3) // -10000000 + 2 => -126

부호가 있는 정수에서 최상위 비트는 부호를 뜻하는 특수한 기능을 한다. 그래서 int8타입 범위가 -128 ~ 127이다.

0111 1111 + 1 = 1000 0000   =>  최상위 비트가 0 -> 1로 바뀌게 됐다.

이처럼 가장 큰 값에 +1을 할 때 가장 작은 값으로 변화하는 현상이 오버플로이다.

 

정수 오버플로

오버플로 반대로 정수 타입이 표현할 수 있는 가장 작은 값에서 -1을 했을 때는 가장 큰 값으로 바뀐다. 

	var y int8 = -128                                // 10000000
	fmt.Printf("%d > %d - 1: %v\n", y, y+1, y > y-1) // 01111111 = 127
	fmt.Printf("y\t = %4d,  %08b\n", y, y)
	fmt.Printf("y-1\t = %4d, %08b\n", y-1, y-1) // 10000000 - 1 = 01111111

정수 타입은 값의 경계에서 오버플로와 언더플로가 발생하기 때문에 연산할 때 항상 경계값에 주의해야한다.

 

float 비교 연산

실수끼리의 == 연산에서 예기치 않는 결과가 나올 때가 있다.

 

6.3 실수 오차

컴퓨터에서 실숫값을 표현할 때 지수부와 소수부로 나눠서 표현한다. 컴퓨터는 지수부와 소수부가 10진수 기준이 아니라 2진수 기준으로 되어있다. 그래서 10진수 실수를 정확히 표현하기 어려운 문제가 있다.

 

작은 오차 무시하기

package main

import "fmt"

const epsilon = 0.000001

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))
}

 

오차를 없애는 더 나은 법

앞서 실행한 오차 없애는 방법은 경우에 따라서 epsilon값이 무시할 만큼 작거나, 그렇지 않기도 하다.

가장 간편하고 좋은 방법은 1비트 차이 만큼 비교하는 것이다.

Go언어에서는 math 패키지에서 Nextafter()함수를 제공한다.

 

6.4 논리 연산자 ( &&, ||, !)

논리 연산자는 불리언 피연산자를 대상으로 연산해 결과로 true나 false를 반환

 

6.5 대입 연산자

대입 연산자는 우변값을 죄변(메모리 공간)에 복사한다.

대입 연산자는 결과를 반환하지 않기 때문에 a = b = 10은 오류를 발생한다.

 

복수 대입 연산자

a, b = 3, 4
a, b = b, a

 

복합 대입 연산자

var a int
a = 3
a += 1 // a = a + 1

값을 반환하지 않는다. b = a+= 2 불가능한 구문이다.

 

증감 연산자

++ : 정수 타입 변수 뒤에 붙여쓰며, 해당 변숫값을 1 증가시킨다.

--: 정수 타입 변수 뒤에 붙여쓰며, 해당 변숫값을 1감소시킨다.

Go언어는 전위 증감 연산자를 지원하지 않는다.

증감문 역시 값을 반환하지 않는다. b = a++는 오류가 발생한다.

반응형
댓글수0