double, float data type 사용 시 주의사항
알티베이스의 double data type 은 C언어의 double 형 데이터타입과 동일합니다. double data type은 고정 소수점 방식보다 넓은 범위의 수를 나타낼 수 있는 반면, 근사값으로 표현되기 때문에
그 값이 정확하지 않을 수 있으며 소숫점 뒤의 의미없는 값들이 지나치게 길게 나올수 있습니다.
또한 double/float 형 값을 iSQL로 조회하거나 iloader 로 export 할 경우 소수점 이하의 값이 잘려서 나오지 않을 수 있습니다.
따라서 소수점이하의 값을 정확하게 조회하거나 저장하기 위해서는 numeric 과 같은 고정 소수점 숫자타입을 사용하여야 합니다.
영향을 받는 버전
모든 알티베이스 버전에 동일하게 해당하는 사항입니다.
double형 숫자값이 잘려서 표현되는 예
iSQL 에서 값이 잘려서 표현되는 현상
iSQL> create table t1 ( c1 double );
Create success.
iSQL> desc t1;
[ TABLESPACE : SYS_TBS_MEM_DATA ]
[ ATTRIBUTE ]
------------------------------------------------------------------------------
NAME TYPE IS NULL
------------------------------------------------------------------------------
C1 DOUBLE FIXED
iSQL> insert into t1 values ( double'100.00000000000001421085471520200372'); <-- double형 칼럼에 double형의 긴 소수점 이하값을 갖는 숫자를 입력한다.
1 row inserted.
iSQL> select c1, to_char(c1) from t1;
C1 TO_CHAR(C1)
--------------------------------------------------------------------------------------------
100 100 <--- 소수점이하의 값이 잘려서 조회
iSQL>
iSQL> select * from t1 where c1 = 100.00000000000001421085471520200372; <-- 조회 조건값이 잘려서 조회되지 않을 수 있다.
C1
-------------------------
No rows selected.
iSQL> select c1, to_char(c1) from t1 where c1 = double'100.00000000000001421085471520200372'; <-- 조회 조건값을 형변환시키면 원하는 값을 찾을 수 있다.
C1 TO_CHAR(C1)
--------------------------------------------------------------------------------------------
100 100 <--- 그러나 조회되서 출력되는 값을 잘려서 표현된다.
1 row selected.
iLoader 에서 data 를 export 했을 때
$ iloader -s localhost -u SYS -p MANAGER out -f SYS_T1.fmt -d SYS_T1.dat -log SYS_T1.log
$ cat SYS_T1.dat
100 <--- 소수점이하가 잘린 값이 export된다.
프로그램에서 double 형 호스트 변수에 가져올 올 경우는 정상 조회된다.
/* select.c */
...................
...................
EXEC SQL BEGIN DECLARE SECTION;
double c1; // select 출력용 double 호스트변수
double ins_c1; // insert 용 double 호스트변수
EXEC SQL END DECLARE SECTION;
ins_c1 = 100.00000000000001421085471520200372;
EXEC SQL INSERT INTO T1 values ( :ins_c1 );
if (sqlca.sqlcode != SQL_SUCCESS)
{
printf("Error : [%d] %s\n\n", SQLCODE, sqlca.sqlerrm.sqlerrmc);
}
EXEC SQL SELECT C1 INTO :c1 FROM T1 limit1;
/* check sqlca.sqlcode */
if (sqlca.sqlcode == SQL_SUCCESS)
{
printf("c1 = %.32f \n", c1);
}
.................
.................
shell> ./select ( 프로그램 실행)
<SELECT>
c1 = 100.00000000000001421085471520200372 <-- 정상적인 값이 출력된다.
Oracle sqlplus 에서도 동일한 현상
확인결과 Oracle 의 sqlplus 에서도 동일하게 값이 잘리는 현상이 있습니다.
SCOTT@orcl> create table t1 ( c1 binary_double );
Table created.
SCOTT@orcl> insert into t1 values ( 100.00000000000001421085471520200372 );
1 row created.
SCOTT@orcl> select c1, to_char(c1) from t1;
C1 TO_CHAR(C1)
---------- ----------------------------------------
1.0E+002 1.0000000000000001E+002
문제 해결방안
소수점이하의 값을 정확하게 조회하거나 저장하기 위해서는 numeric(scale, presicion ) 과 같은 고정 소수점 숫자타입을 사용하여야 합니다.
아래는 numeric 형을 사용할 경우 iSQL에서 값이 정상 조회되는 실행 예입니다.
iSQL> create table t2 ( c1 numeric(35, 32 ) );
Create success.
iSQL> desc t2;
[ TABLESPACE : SYS_TBS_MEM_DATA ]
[ ATTRIBUTE ]
------------------------------------------------------------------------------
NAME TYPE IS NULL
------------------------------------------------------------------------------
C1 NUMERIC(35, 32) FIXED
iSQL> insert into t2 values ( 100.00000000000001421085471520200372 );
iSQL> insert into t2 values ( 123.00000000000001421085471520200372 );
iSQL> insert into t2 values ( 100.12345 );
iSQL> select c1, to_char(c1) from t2; <-- iSQL 상에서 소수점 이하 값을 그대로 표현하기 위해서 to_char 함수를 사용하여 char 형으로 형변환합니다.
C1 TO_CHAR(C1)
---------------------------------------------------------------------------------
100 100.00000000000001421085471520200372
123 123.00000000000001421085471520200372
100.12345 100.12345
iSQL> select c1, to_char(c1) from t2 where c1 = 100.00000000000001421085471520200372; <-- 조건값으로 조회할 경우 별도의 형변환이 필요없이 사용 가능합니다.
C1 TO_CHAR(C1)
---------------------------------------------------------------------------------
100 100.00000000000001421085471520200372
1 row selected.