16진수 HEX 값을 10진수 DECIMAL 값으로 변환하는 함수(2의보수)


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;
/

by 허둥사마 | 2011/07/15 10:41 | DB(ORACLE) | 트랙백 | 덧글(0)
트랙백 주소 : http://tit99hds.egloos.com/tb/3200922
☞ 내 이글루에 이 글과 관련된 글 쓰기 (트랙백 보내기) [도움말]

:         :

:

비공개 덧글



< 이전페이지 다음페이지 >