원본 : http://tit99hds.egloos.com/3200922

 

안녕하세요, 첨 글을 남겨봅니다.

MS-SQL로 개발하다 오라클로 바꾼지 3개월 정도 되었네요.

제목대로 16진수 값을 음의정수를 지원하는 10진수로 변환하는 방법을 찾다가 찾지 못하여 직접 구현한 소스 올립니다.

저와 같은 고민하시는 분들께 도움이 되었으면 합니다.

아래 글은 원본글입니다.

감사합니다.

 

----------------------------------------------------------------------------------------------------------------------

 

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