MS-SQL에서는 16진수 값을 음수값을 지원하는 10진수로 변환하기 위해
SELECT CAST(0xAF308B AS INT) 와 같은 형태로 제공한다.
오라클에서 이와 같은 기능을 검색해 보았지만 음수를 지원하지 않고 양수만 지원하는 함수를 제공한다.
결국 직접 구현하는 방법 외에는 발견되지 않았다.
구현 원리는 16진수 값을 2진수로 변환하여 젤 첫 바이트(0xAF308B 라면 A) 값의 첫번째 비트(1010)가 1이라면 음수이고
2의 보수를 적용하여 음수값을 표현하도록 함수를 작성하였다.
2의 보수 계산 법은 0이면 1, 1이면 0으로 변환한 후 1을 더하는 방식이다.
2진수 형태의 연산 방식이 있을 것으로 판단했지만 찾기가 쉽지 않아 문자열 처리하여 변환하였다.
16진수 hex값으로 2진수 binary 값을 추출한 후 2진수 값으로 10진수 decimal 값을 변환하는 방식으로 구현되었다.
SELECT UFN_HEX2DEC('49CF90CB') FROM DUAL
결과 : 1238339787
SELECT UFN_HEX2DEC('9B83A77A') FROM DUAL
결과 : -1685870726
CREATE OR REPLACE FUNCTION UFN_HEX2DEC (HEXVAL IN CHAR) RETURN NUMBER IS
-- 16진수를 10진수로 변환( 음의정수로 출력 )
RESULT NUMBER := 0;
V_BIN VARCHAR2(200);
V_DEC NUMBER;
DIGITS NUMBER;
BEGIN
V_BIN := UFN_HEX2BIN(HEXVAL); -- 16진수 값을 2진수로 변환
DIGITS := LENGTH(V_BIN);
IF MOD(DIGITS,4) = 0 THEN -- 2진수 값은 4의 배수여야 한다.(앞에 0 포함)
IF SUBSTR(V_BIN, 1, 1) = '1' THEN -- 첫번째 bit 값이 1이면.. 음수
V_BIN := UFN_COMPLEMENTOFTWO(V_BIN); -- 2의보수 적용
RESULT := TO_NUMBER('-' || UFN_BIN2DEC(V_BIN)); -- 마이너스(-) 표시 후 10진수 변환
ELSE
RESULT := UFN_BIN2DEC(V_BIN); -- 2진수 값을 10진수로 변환
END IF;
END IF;
RETURN RESULT;
END UFN_HEX2DEC;
/
CREATE OR REPLACE FUNCTION UFN_HEX2BIN (HEXVAL IN CHAR) RETURN VARCHAR IS
-- 16진수를 2진수로 변환
I NUMBER;
DIGITS NUMBER;
RESULT VARCHAR(100);
CURRENT_DIGIT CHAR(1);
CURRENT_DIGIT_DEC NUMBER;
BEGIN
DIGITS := LENGTH(HEXVAL);
FOR I IN 1..DIGITS LOOP
CURRENT_DIGIT := SUBSTR(HEXVAL, I, 1);
IF CURRENT_DIGIT IN ('A','B','C','D','E','F') THEN -- A~F 값 정의
CURRENT_DIGIT_DEC := ASCII(CURRENT_DIGIT) - ASCII('A') + 10;
ELSE
CURRENT_DIGIT_DEC := TO_NUMBER(CURRENT_DIGIT);
END IF;
RESULT := RESULT || UFN_DEC2BIN(CURRENT_DIGIT_DEC); -- 각 자리값을 2진수로 변환
END LOOP;
RETURN RESULT;
END UFN_HEX2BIN;
/
CREATE OR REPLACE FUNCTION UFN_DEC2BIN ( I_NUM IN PLS_INTEGER ) RETURN VARCHAR2 IS
-- 10진수를 2진수로 변환
L_NUM PLS_INTEGER;
L_BIT PLS_INTEGER;
L_BINARY VARCHAR2(128);
BEGIN
L_NUM := I_NUM;
WHILE L_NUM > 1 LOOP
L_BIT := MOD(L_NUM,2);
L_BINARY := TO_CHAR(L_BIT)||L_BINARY;
L_NUM := FLOOR(L_NUM / 2);
END LOOP;
IF L_NUM = 1 THEN
L_BINARY := '1'||L_BINARY;
END IF;
IF I_NUM = 0 THEN
L_BINARY := '0000'; -- 10진수 값이 0 이라면 0000
ELSE
L_BINARY := LPAD(L_BINARY, 4, '0'); -- 10진수 값이 7이하라면 앞자리 0으로 채움
END IF;
RETURN LPAD(L_BINARY, 4, '0');
END UFN_DEC2BIN;
/
CREATE OR REPLACE FUNCTION UFN_COMPLEMENTOFTWO (BINVAL IN VARCHAR) RETURN VARCHAR IS
-- 2진수를 2의보수 변환
I NUMBER;
DIGITS NUMBER;
RESULT VARCHAR2(200);
V_CHAR VARCHAR2(200);
CURRENT_DIGIT CHAR(1);
CURRENT_DIGIT_DEC NUMBER;
V_CHK NUMBER := 0; -- 2의 보수 계산을 위한 변수
BEGIN
DIGITS := LENGTH(BINVAL);
FOR I IN 1..DIGITS LOOP -- 1의 보수 계산(0이면 1, 1이면 0)
CURRENT_DIGIT := SUBSTR(BINVAL, I, 1);
IF CURRENT_DIGIT = '0' THEN
CURRENT_DIGIT := '1'; -- 0이면 1로..
ELSIF CURRENT_DIGIT = '1' THEN
CURRENT_DIGIT := '0'; -- 1이면 0으로..
END IF;
RESULT := RESULT || CURRENT_DIGIT;
END LOOP;
I := DIGITS;
WHILE I > 0 LOOP -- 2의 보수 계산을 위해 1의 보수값 + 1, 끝자리부터 연산
IF V_CHK = 1 THEN
EXIT;
END IF;
IF SUBSTR(RESULT, I, 1) = '1' THEN -- 비트값이 1이면 0으로 변경 후 다음 단계 반복.
V_CHAR := '0' || V_CHAR;
ELSIF SUBSTR(RESULT, I, 1) = '0' THEN -- 비트값이 0이면 1로 변경 후 체크값 변수 할당
V_CHAR := '1' || V_CHAR;
V_CHK := 1;
END IF;
I := I - 1;
END LOOP;
RESULT := SUBSTR(RESULT, 1, DIGITS - LENGTH(V_CHAR)) || V_CHAR; -- 1의 보수 앞자리 + 2의 보수 계산값 concat
RETURN RESULT;
END UFN_COMPLEMENTOFTWO;
/
CREATE OR REPLACE FUNCTION UFN_BIN2DEC (BINVAL IN CHAR) RETURN NUMBER IS
-- 2진수를 10진수로 변환
I NUMBER;
DIGITS NUMBER;
RESULT NUMBER := 0;
CURRENT_DIGIT CHAR(1);
CURRENT_DIGIT_DEC NUMBER;
BEGIN
DIGITS := LENGTH(BINVAL);
FOR I IN 1..DIGITS LOOP
CURRENT_DIGIT := SUBSTR(BINVAL, I, 1);
CURRENT_DIGIT_DEC := TO_NUMBER(CURRENT_DIGIT);
RESULT := (RESULT * 2) + CURRENT_DIGIT_DEC;
END LOOP;
RETURN RESULT;
END UFN_BIN2DEC;
/