Bitwise functions

and ( v1, v2 [ , ... ] )

Return the bitwise AND of the arguments. There must be at least two.

or ( v1, v2 [ , ... ] )

Return the bitwise OR of the arguments. There must be at least two.

xor ( v1, v2 [ , ... ] )

Return the bitwise XOR of the arguments. There must be at least two.

lshift ( val, count )

Return the value of val, shifted left by count bits.

rshift ( val, count )

Return the value of val, shifted right by count bits.

compl ( val )

Return the bitwise complement of val.


1. lshift 의 경우 오른쪽이 0 으로 채워지고

2. rshift 의 경우 왼쪽이 0 으로 채워집니다.

3. 인수로 음수는 사용할 수 없습니다.

4. awk 는 비트연산에 64 bits 가 사용됩니다.

# 185 = 1011 1001
# 92  = 0101 1100   rshift 1           # 왼쪽이 0 으로 채워진다.
# 46  = 0010 1110   rshift 2
# 23  = 0001 0111   rshift 3
$ awk 'BEGIN { print rshift(185,3) }'
23

# 185  = 1011 1001
# 370  = 0001 0111 0010   lshift 1     # 오른쪽이 0 으로 채워진다.
# 740  = 0010 1110 0100   lshift 2
# 1480 = 0101 1100 1000   lshift 3
$ awk 'BEGIN { print lshift(185,3) }'
1480

# 1480 = 0101 1100 1000
# 0xff = 0000 1111 1111
# and  =      1100 1000 (200)
$ awk 'BEGIN { print and(lshift(185,3),0xff) }' 
200

awk 는 기본적으로 숫자를 double floating point 로 처리합니다.

그러므로 정수값은 53 bits 크기 까지만 정확도가 있습니다. 다음을 보면 53 bits 를 모두 1 로 채웠을 때 ( 1FFFFFFFFFFFFF ) 의 정수값 9007199254740991 은 정상적으로 출력이 되지만 54 bits ( 3FFFFFFFFFFFFF ) 부터는 출력값이 입력과 다른 것을 볼 수 있습니다. ( 이것이 C 프로그래밍을 할때 같은 8 bytes 크기라도 53 bits 이상의 long 값을 double 타입으로 캐스팅해 사용하게 되면 정확도의 손실이 생길 수 있는 이유입니다. )

##################  53 bits  #################

$ awk  'BEGIN { a=9007199254740991; print a }' 
9007199254740991

##################  54 bits  #################

$ awk  'BEGIN { a=18014398509481983; print a }'     # 입력값과 출력값이 다르다.
18014398509481984

$ awk  'BEGIN { a=18014398509481983; print a+1 }'   
18014398509481984

$ awk  'BEGIN { a=18014398509481983; print a+2 }'   # +2 결과도 정확하지 않다.
18014398509481984

$ awk  'BEGIN { a=18014398509481983; print a+3 }'   # 1986 이 안되고 1988 이 된다.
18014398509481988

따라서 compl 연산은 다음과 같은 차이가 있으므로 필요에 따라 -M 옵션을 사용해야 합니다.

# 64 bits 값이 모두 표시되지 않는다. 
# ( 1 의 보수 이므로 0xfff... 로 시작해야 되는데 0x3ff... 로 시작한다 )
$ awk 'BEGIN { printf "%#x %u\n", compl(1), compl(1); print compl(1)  }'
0x3ffffffffffffe 18014398509481982
18014398509481982

# -M 옵션을 사용하면 64 bits 값이 모두 표시되고 
# sign bit 에의해 10 진수가 음수로 표시되는 것을 볼 수 있습니다.
$ awk -M 'BEGIN { printf "%#x %u\n", compl(1), compl(1); print compl(1)  }'
0xfffffffffffffffe 18446744073709551614
-2

Quiz

값을 2 진수로 출력하려면 어떻게 할까요?

$ awk -f - <<\EOF
BEGIN {
    printf "123 = %s\n", bits2str(123)
    printf "0123 = %s\n", bits2str(0123)   # 0123 은 8 진수
    printf "0x99 = %s\n", bits2str(0x99)

    shift = lshift(0x99, 2)
    printf "lshift(0x99, 2) = %#x = %s\n", shift, bits2str(shift)
    shift = rshift(0x99, 2)
    printf "rshift(0x99, 2) = %#x = %s\n", shift, bits2str(shift)
}

function bits2str(bits,        data, mask)
{
    if (bits == 0)
        return 0

    for ( mask = count = 1; count <= 64; count++ ) {
        data = (and(bits, mask) ? "1" : "0") data
        # 최종 bits 값이 0 이 될 때까지 rshift
        if ((bits = rshift( bits, 1)) == 0) break   
    }

    while (length(data) % 8)            # 바이너리 문자 개수를 바이트 단위로 맞춤
        data = "0" data

    return data
}
EOF

123 = 01111011
0123 = 01010011
0x99 = 10011001
lshift(0x99, 2) = 0x264 = 0000001001100100
rshift(0x99, 2) = 0x26 = 00100110