YOGYUI

Python - 음의 정수를 16진수로 표현하기 (negative int to hex) 본문

Software/Python

Python - 음의 정수를 16진수로 표현하기 (negative int to hex)

요겨 2021. 8. 24. 14:31
반응형

 

Python 내장함수인 hex는 정수값을 16진수로 표현된 문자열(str)을 반환해준다

help(hex)
>> 
Help on built-in function hex in module builtins:

hex(number, /)
    Return the hexadecimal representation of an integer.
    
hex(12648430)
    '0xc0ffee'
In [1]: hex(25)
Out[1]: '0x19'

In [2]: hex(1234)
Out[2]: '0x4d2'

그런데, 함수의 인자로 음의 정수 (negative integer)를 입력하면 다른 프로그래밍 언어에서와는 다른 결과가 출력된다

In [3]: hex(-1)
Out[3]: '-0x1'

In [4]: hex(-128)
Out[4]: '-0x80'

C/C++과 같이 자료형의 크기를 사전에 설정하는 언어에서는 위의 예에서

  • 1바이트(8bit)의 경우 -1 = 0xFF, -128 = 0x80 
  • 4바이트(32bit)의 경우 -1 = 0xFFFFFFFF, -128 = 0xFFFFFF80

이 되어야 한데, hex 함수는 정수형을 몇 바이트 자료형으로 처리할 지에 대한 개념이 없으니 그냥 속편하게 절대값을 취한 뒤 - 기호를 붙여서 반환해버린다

 

음수가 왜 위와 같이 표현되어야 하는지는 '2의 보수 (2's complement)'와 관련있는데, 프로그래머라면 반드시 알아야 하는 개념이니 remind하도록 하자

https://ko.wikipedia.org/wiki/2%EC%9D%98_%EB%B3%B4%EC%88%98

 

2의 보수 - 위키백과, 우리 모두의 백과사전

2의 보수(--補數, 영어: two's complement)란 어떤 수를 커다란 2의 제곱수에서 빼서 얻은 이진수이다. 2의 보수는 대부분의 산술연산에서 원래 숫자의 음수처럼 취급된다. 주어진 이진수보다 한 자리

ko.wikipedia.org

 

구글링해보니 2가지 방식이 흔히 쓰이는데, 편한 방식을 골라서 사용하면 된다

(핵심은 정수값을 몇 바이트로 처리해야하는지를 함께 기입)

1. Bit Shifting

def tohex1(value: int, nbit: int):
    conv = (value + (1 << nbit)) % (1 << nbit)
    return hex(conv)
In [5]: tohex1(-1, 8)
Out[5]: '0xff'

In [6]: tohex1(-128, 8)
Out[6]: '0x80'

In [7]: tohex1(127, 8)
Out[7]: '0x7f'

2. Bit Masking

def tohex2(value: int, nbit: int):
    conv = (value & (2 ** nbit - 1))
    return hex(conv)
In [8]: tohex2(-1, 8)
Out[8]: '0xff'

In [9]: tohex2(-128, 8)
Out[9]: '0x80'

In [10]: tohex2(127, 8)
Out[10]: '0x7f'

 

Bit Shifting이 Bit Masking보다 수행속도가 약간 빠르니 1번을 추천한다

import time

tm = time.perf_counter()
for _ in range(100000):
    tohex1(-1, 8)
elapsed1 = time.perf_counter() - tm

tm = time.perf_counter()
for _ in range(100000):
    tohex2(-1, 8)
elapsed2 = time.perf_counter() - tm
In [11]: elapsed1
Out[11]: 0.05493220000062138

In [12]: elapsed2
Out[12]: 0.08496230000309879

3. Etc

위 2개의 구현 모두 범위를 벗어나는 경우에 대한 예외처리가 없다

In [13]: tohex1(-128, 8)
Out[13]: '0x80'

In [14]: tohex1(128, 8)
Out[14]: '0x80'

8비트 signed 정수형이 표현할 수 있는 값의 범위는 -128 ~ +127 까지이므로 (128, 8)에 대해서는 예외처리를 해주는 게 바람직하다

def tohex1(value: int, nbit: int):
    if -2 ** (nbit - 1) <= value <= 2 ** (nbit - 1) - 1:
        conv = (value + (1 << nbit)) % (1 << nbit)
        return hex(conv)
    else:
        raise ValueError

 

 

str의 format 으로도 위와 유사하게 구현할 수 있다

def tohex3(value: int, nbit: int):
    if -2 ** (nbit - 1) <= value <= 2 ** (nbit - 1) - 1:
        conv = (value + (1 << nbit)) % (1 << nbit)
        return '{:X}'.format(conv)
    else:
        raise ValueError

어떤 형식으로 포매팅할 지는 구현하는 사람 마음대로~

 

끝~!

 

[참고]

https://www.py4u.net/discuss/141006

반응형