연산자

1. 산술 연산자

구분연산자연산피연산자 타입
사칙 연산과 나머지+덧셈정수, 실수, 복소수, 문자열
-뺄셈정수, 실수, 복소수
*곱셈정수, 실수, 복소수
/나눗셈정수, 실수, 복소수
%나머지정수, 실수, 복소수
비트 연산&AND 비트연산정수
|OR비트 연산정수
^XOR비트 연산정수
&^비트 클리어정수
시프트 연산«왼쪽 시프트정수 « 양의 정수
»오른쪽 시프트정수 » 양의 정수

산술 연산은 다른 언어의 연산과 별로 다를 것이 없으며 go는 강타입 언어이기 때문에 반드시 피연산자들끼리의 타입이 같아야만 에러가 발생하지 않는다.


1) ^

a := uint8(1)
fmt.Printf("%08b",a)  //00000001
fmt.Printf("%08b",^a) //11111110

비트 연산중에 **^(XOR)**은 혼자쓰이면 비트 반전(1의 보수,NOT)으로 사용될 수 있다.


2) &^

a := uint8(255)
fmt.Printf("%08b \n",a) //11111111
fmt.Printf("%08b \n",a&^2) //11111101
fmt.Printf("%08b \n",a&^8) //11110111

&^ 연산은 비트 클리어연산으로 우측 피연산자를 ^(NOT) 으로 사용된 후 양쪽 피연산자에 대해 & 를 수행한 것으로 왼쪽 피연산자 비트에서 오른족 피연산자의 1의 값을 갖는 위치의 비트를 0으로 바꾸고 싶을 때 사용한다.


3) 시프트 연산

a := uint8(1)                     //00000001
b := int8(-1)                     //11111111
fmt.Printf("%08b \n",a<<4)        //00010000
fmt.Printf("%08b \n",uint8(b<<4)) //11110000
fmt.Printf("%08b \n",a>>1)        //00000000
fmt.Printf("%08b \n",uint8(b>>4)) //11111111

« 연산은 비트를 왼쪽으로 밀고 새로 채우는 비트는 무조건 0으로 채운다.

» 연산은 왼쪽피연산자의 부호에 따라 새로 채우는 비트의 값이 달라지며, 부호가 없는 정수라면 0, 음수라면 1로 채워진다.



2. 비교 연산

연산자설명
==같다
!=같지 않다
<작다
>크다
<=작거나 같다
>=크거나 같다

1) 실수 비교

a := 0.1
b := 0.2
c := 0.3
d := a+b

fmt.Println(c == d)   //false
fmt.Println("c : "+ strconv.FormatFloat(c,'f',-1,64)) //0.3
fmt.Println("d : "+ strconv.FormatFloat(d,'f',-1,64)) //0.30000000000000004

실수 계산시 부동소수점 방식은 오차가 발생할 수 있어 원하는 결과값을 도출해내지 못할 수 있기때문에 특정 범위까지의 오차를 지정해 무시하거나 패키지를 이용해서 정밀하게 연산을 수행하는 방법을 택해야 한다.


특정 범위 지정

const floatRange = 0.000001

func equal(a,b float64) bool{
    if a>b{
        if a-b > floatRange{
          return false;
        }
        return true;
    }else{
        if b-a > floatRange {
            return false;
        }
        return true;
    }
}

func main() {
    a := 0.1
    b := 0.2
    c := 0.3
    d := a+b

    fmt.Println(equal(c,d)) //true
}

오차를 허용할 범위를 상수로 선언을 한 후에 별도의 함수를 만들어 비교하는 방법을 이용할 수 있다.


Float객체

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 == d)   //false
    fmt.Println(c.Cmp(d)) //0
}

big의 Float객체를 이용해서 소수를 표현하고 산술/비교연산을 수행하면 정밀한 오차까지 계산을 수행할 수 있으며, 해당 변수는 객체이기 때문에 == 연산은 false가 되고 값 비교는 Cmp() 메서드를 이용하면 되고 Java의 compareTo와 비슷하게 주체가 크면 1, 작으면 -1, 같으면 0을 반환한다.



3. 논리 연산

종류연산자항 개수설명
논리&&다항 연산자양변 모두 true면 true
| |다항 연산자양변 중 하나라도 true면 true
!단항 연산자true이면 false, false이면 true

func setD(d *int) int{
    *d= 3
    return *d
}

func main() {
    c := 3
    d := 0

    fmt.Println( true || setD(&d) == c)  //true
    fmt.Println(d)    //0

    fmt.Println( false || setD(&d) == c)  //true
    fmt.Println(d)    //3
}

논리 연산시 단락 회로 평가(쇼트 서킷) 로 왼쪽 피연산자가 true나 false조건이 만족한다면 오른쪽 피연산자를 참조하지 않고 바로 return하게 된다.

예를 들어 && 연산을 수행할때 왼쪽 피연산자가 false라면 오른쪽 피연산자의 결과에 상관없이 false를, || 연산은 왼쪽이 true라면 바로 true를 리턴하게 된다.



4. 대입 연산

var a int
var b int
a = b = 1   //error

/*success*/
a = 1
b = 1

대입 연산은 다른 언어들과 거의 동일하며 복합 대입연산자도 지원을 하지만 특정 문법의 제약이 강해 위와 같은 방법으로 대입은 불가능하다.





Reference

『Tucker의 Go 언어 프로그래밍』 스터디 요약 노트

Tags :

Related Posts

페이징 기능 추가하기

페이징 기능 추가하기

각종 css와 shortcode들을 추가했던 이전 글 에 이어 이번에는 pagination을 추가해보려고 한다. 저는 각 카테고리의 메인 페이지에서는 해당 카테고리의 글들을 paging처리하여 모두 볼 수 있게 구성을 하고 싶었는 데, 제가 사용하고 있는 docport 테마는 paging처리가 들어있지 않은 테마였다. 그래서 hugo 공식사이트 를 참고하여 적용시킨 방법을 공유하려고 한다....

Read More
포인터

포인터

메모리 주소를 값으로 갖는 데이터 타입 1. 선언 방법 메모리주소를 가리킬 데이터타입형에 *를 붙이면 해당 타입의 메모리주소를 담는 포인트형을 선언 할 수 있다. &amp; 를 이용해서 변수의 메모리주소 시작값을 할당 할 수 있다. 메모리 주소 시작값은 하나의 값으로 일종의 숫자 값이다. 2. 사용 방법 포인터 변수를 선언할때 말고 사용하는 시점에 변수앞에 *를 붙이면 메모리주소에 들어있는 값을 의미하고 이를 이용해 메모리주소에 값을 할당 할 수 있다....

Read More
Slice

Slice

컴파일타임에 데이터 크기가 고정되어 런타임에 변경이 되지 않는 일반 배열과 달리 변경이 가능한 동적 배열 타입을 slice라고 한다. 정확하게 얘기하면 go에서 제공하는 배열을 가리키는 포인터 타입이다. 1. 선언 방법 배열선언방식과 비슷하지만 [] 안에 배열크기를 지정하지 않으면 slice로 선언이 된다....

Read More