연산자

1. 산술 연산자

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

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


^

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

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


◾ &^

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으로 바꾸고 싶을 때 사용한다.


시프트 연산

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. 비교 연산

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

◾ 실수 비교

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

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


1. 특정 범위 지정

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
}

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


2. 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 언어 프로그래밍』 스터디 요약 노트