빌드노트
PostgreSQL에서 round(double precision, 2)가 없다는 오류가 날 때
PostgreSQL에서 round(double precision, 2)가 실패하는 이유를 실제 타입과 내장 오버로드로 확인한다. numeric 변환의 효과와 한계, 동점 반올림 차이, 두 자리 표시용 to_char를 작은 실행 결과로 구분한다.
PostgreSQL에서 double precision 계산값에 round(value, 2)를 적용하면 function round(double precision, integer) does not exist 오류가 난다. 소수 자릿수를 받는 내장 함수는 round(numeric, integer)이기 때문이다. 현재 계산값을 반올림하려면 round(value::numeric, 2)로 호출할 수 있다. 다만 이 변환이 앞선 부동소수 계산까지 정확하게 바꾸지는 않는다. PostgreSQL 18의 round 함수 (새 탭에서 열림)
round 바로 안에 들어가는 식의 타입을 확인한다
자신의 조회에서 round의 첫 인자를 pg_typeof(...)에 넣는다. 열이나 집계식이면 원래 FROM과 GROUP BY는 유지하고, 열 선언만 보지 말고 실제 계산식의 타입을 확인한다. pg_typeof 설명 (새 탭에서 열림)
아래는 PostgreSQL에 연결된 psql이나 SQL 편집기에서 테이블 없이 실행할 수 있는 비교다. 리터럴과 부동소수 계산식이 같은 타입인지 확인한다.
SELECT pg_typeof(12.345) AS literal_type,
pg_typeof(12.345::double precision * 1.0) AS expression_type;
새 전용 DB의 PostgreSQL 18.6, Linux x86_64에서 결과는 literal_type = numeric, expression_type = double precision이었다. 별도로 실행한 round(12.345, 2)는 12.35를 반환했지만, SELECT round(12.345::double precision * 1.0, 2);는 SQLSTATE 42883과 위 오류를 냈다. 실패 문장을 확인하려면 다른 조회와 따로 실행한다. 숫자가 비슷하게 보여도 함수에 전달되는 타입은 다를 수 있다.
같은 DB에서 확인한 내장 형태는 round(double precision) → double precision, round(numeric) → numeric, round(numeric, integer) → numeric의 세 가지였다. 이름이 같아도 인자 개수와 타입이 맞아야 한다. 두 번째 인자를 없애면 정수 단위 반올림이 된다. 위 부동소수 식에 인자 하나짜리 round를 적용한 결과는 12였으므로 소수 둘째 자리 반올림을 대신하지 못한다.
numeric 변환으로 해결되는 범위를 구분한다
이미 계산된 근삿값을 받아 소수 둘째 자리로 반올림할 목적이면 식 전체를 numeric으로 변환한다. 다음 조회로 최소 수정 뒤의 값과 반환 타입을 확인한다.
SELECT round((12.345::double precision * 1.0)::numeric, 2) AS rounded,
pg_typeof(round((12.345::double precision * 1.0)::numeric, 2)) AS result_type;
결과는 rounded = 12.35, result_type = numeric이었다. 자신의 조회에서는 상수 식 자리에 원래 계산식을 넣어 round((계산식)::numeric, 2)처럼 적용한다. 괄호는 복합식 전체가 변환되도록 하기 위한 것이다. 반환 타입도 바뀌므로 결과를 받는 코드의 numeric 처리도 확인한다. 첫 인자가 이미 numeric인데 같은 오류가 난다면 오류에 표시된 두 번째 인자 타입이 integer인지부터 대조한다.
십진수 입력으로 정확하게 계산하는 것이 목적이면 입력부터 numeric을 쓰는 경로를 검토한다. double precision은 일부 십진수를 근삿값으로 저장하고 그 값으로 계산한다. 이미 부동소수 열에 저장한 값을 계산 전에 캐스팅하더라도 저장 전의 원래 십진 입력이 복원되지는 않는다. numeric과 부동소수 타입의 차이 (새 탭에서 열림)
작은 합성 값으로 0.1 + 0.2 - 0.3을 비교했다. 세 입력을 모두 double precision으로 계산한 뒤 결과를 numeric으로 바꾸면 0.0000000000000000555111512312578이 남았고, 처음부터 모두 numeric이면 0.0이었다. 둘을 소수 둘째 자리로 반올림해 같게 보이도록 할 수 있어도 앞선 계산이 같아지는 것은 아니다.
동점 처리도 확인해야 한다. numeric은 정확히 중간인 값을 0에서 멀어지는 쪽으로 반올림한다. 소수 둘째 자리 비교에서 1.124, 1.125, 1.126은 각각 1.12, 1.13, 1.13이었고, -1.124, -1.125, -1.126은 각각 -1.12, -1.13, -1.13이었다. round의 동점 처리 (새 탭에서 열림)
round(value * 100) / 100으로 우회하면 부동소수 동점 규칙이 적용될 수 있다. 이 환경에서는 이진수로도 정확히 표현되는 1.125::double precision에 그 식을 적용하면 1.12, round(1.125::numeric, 2)는 1.13이었다. 인자 하나짜리 round에서도 부동소수 2.5와 -2.5는 각각 2, -2였고, numeric은 3, -3이었다. 공식 문서는 부동소수 동점 처리가 플랫폼에 따라 다르며 가까운 짝수 쪽이 흔하다고 설명한다. 이 관찰을 모든 OS의 결과로 확대할 수는 없다.
두 자리 표기만 필요하면 to_char를 쓴다
후속 계산용 숫자가 아니라 12.3을 12.30으로 보여 줄 문자열이 필요하면 to_char를 사용할 수 있다. 다음 조회로 표시값과 반환 타입을 함께 확인한다.
SELECT to_char(12.3::double precision, 'FM999990.00') AS display,
pg_typeof(to_char(12.3::double precision, 'FM999990.00')) AS display_type;
결과는 display = 12.30, display_type = text였다. FM은 채움 공백을 없애고, 소수점 뒤 00은 두 자리를 표시한다. 서식보다 소수 자릿수가 많으면 표시 과정에서도 반올림된다. 같은 패턴으로 실행한 12.345::double precision은 12.35였다. 원래 숫자의 타입은 그대로이며 출력 문자열을 계산·숫자 정렬의 기준으로 돌려 쓰지 않는다. 숫자 서식 함수와 패턴 (새 탭에서 열림)
정수부 폭은 값에 맞춰 정해야 한다. 위 패턴은 정수부 여섯 자리용이다. 일곱 자리인 1234567을 넣은 결과는 ######.##였으므로 모든 크기의 수에 그대로 적용할 수는 없다.
이번 실행은 PostgreSQL 18.6의 내장 함수와 작은 합성 값에 한정한다. 실제 열의 정밀도·스케일 제약, 드라이버가 보내는 매개변수 타입, 다른 OS의 부동소수 동점 처리는 검증하지 않았다.