“SELECT POWER (10.0, 38.0);”가 산술 오버플로 오류를 발생시키는 이유는 무엇입니까?


15

나는 업데이트하고있어 IDENTITY오버 플로우 검사 스크립트를 위한 계정 DECIMALNUMERIC IDENTITY .

확인의 일부로 모든 IDENTITY열에 대한 데이터 유형 범위의 크기를 계산합니다 . 나는 그것을 사용하여 해당 범위의 몇 퍼센트가 소진되었는지 계산합니다. 들어 DECIMALNUMERIC 그 범위의 크기는2 * 10^p - 2 어디 p정밀도입니다.

DECIMALNUMERIC IDENTITY열 이있는 테스트 테이블을 만들고 다음과 같이 범위를 계산하려고했습니다.

SELECT POWER(10.0, precision)
FROM sys.columns
WHERE 
       is_identity = 1
   AND type_is_decimal_or_numeric
;

이로 인해 다음과 같은 오류가 발생했습니다.

Msg 8115, Level 16, State 6, Line 1
Arithmetic overflow error converting float to data type numeric. 

나는 그것을 IDENTITY유형 의 열로 좁혔습니다 DECIMAL(38, 0)(즉, 최대 정밀도로). 그런 다음 POWER()그 값에서 직접 계산 을 시도했습니다 .

다음 쿼리 모두

SELECT POWER(10.0, 38.0);
SELECT CONVERT(FLOAT, (POWER(10.0, 38.0)));
SELECT CAST(POWER(10.0, 38.0) AS FLOAT);

또한 같은 오류가 발생했습니다.

  • SQL Server POWER()가 유형 의 출력 FLOATNUMERIC(특히 FLOAT우선 순위가 높은 경우) 로 변환하려고하는 이유는 무엇 입니까?
  • 어떻게 동적으로는 A의 범위를 계산할 수 있습니다 DECIMAL또는 NUMERIC모든 가능한 정밀도 (포함에 대한 열 p = 38물론 등)?

답변:


18

로부터 POWER문서 :

통사론

POWER ( float_expression , y )

인수

float_expression float 유형 또는 암시 적으로 float 로 변환 할 수있는 유형
표현식 입니다 .

y float_expression
을 올릴 수있는 힘 입니다. y비트 데이터 유형을 제외하고 정확한 숫자 또는 대략적인 숫자 데이터 유형 범주의 표현식 일 수 있습니다 .

반품 유형

float_expression에 제출 된 것과 동일한 유형을 반환합니다 . 예를 들어 10 진수 (2,0)가 float_expression으로 제출되면 반환 된 결과는 10 진수 (2,0)입니다.


첫 번째 입력은 암시 적으로 float필요한 경우 .

내부 계산은 float표준 CRT (C 런타임 라이브러리) 함수에 의한 산술을 사용하여 수행 됩니다.pow .

그런 다음 의 float출력 pow은 왼쪽 피연산자의 유형으로 다시 캐스팅됩니다 (numeric(3,1) 리터럴 값 10.0을 사용하는 경우에 포함됨).

float귀하의 경우 명시 적으로 사용하면 좋습니다 .

SELECT POWER(1e1, 38);
SELECT POWER(CAST(10 as float), 38.0);

10 38 의 정확한 결과 decimal/numeric는 39 자리의 정밀도 (1 뒤에 38 0)가 필요하기 때문에 SQL Server에 저장할 수 없습니다 . 최대 정밀도는 38입니다.


23

더 이상 Martin의 대답을 다루지 않고 POWER()여기 에 관한 나머지 발견을 추가 할 것입니다.

당신의 팬티를 붙잡 으십시오.

전문

먼저, 다음에 대한 MSDN 설명서 인POWER() A를 전시 합니다 .

통사론

POWER ( float_expression , y )

인수

float_expression float 유형 또는 암시 적으로 float로 변환 할 수있는 유형의 표현식입니다.

반품 유형

와 동일합니다 float_expression.

마지막 행을 읽은 후 POWER()리턴 유형이 FLOAT이지만 다시 읽은 것으로 결론을 내릴 수 있습니다 . float_expression"float 유형이거나 내재적으로 float로 변환 할 수있는 유형"입니다. 따라서 이름에도 불구하고 float_expression실제로는 FLOAT, a DECIMAL또는 a 일 수 있습니다 INT. 의 출력은의 출력과 POWER()동일하므로 float_expression해당 유형 중 하나 일 수도 있습니다.

따라서 입력에 따라 반환 유형이있는 스칼라 함수가 있습니다. 그럴 수 있습니까?

관찰

나는 당신 POWER()에게 입력에 따라 출력을 다른 데이터 유형으로 캐스팅하는 것을 보여주는 테스트 인 B를 보여줍니다 .

SELECT 
    POWER(10, 3)             AS int
  , POWER(1000000000000, 3)  AS numeric0     -- one trillion
  , POWER(10.0, 3)           AS numeric1
  , POWER(10.12305, 3)       AS numeric5
  , POWER(1e1, 3)            AS float
INTO power_test;

EXECUTE sp_help power_test;

DROP TABLE power_test;

관련 결과는 다음과 같습니다.

Column_name    Type      Length    Prec     Scale
-------------------------------------------------
int            int       4         10       0
numeric0       numeric   17        38       0
numeric1       numeric   17        38       1
numeric5       numeric   17        38       5
float          float     8         53       NULL

일어나는 것처럼 보이는 것은 포함하지 않는 가장 작은 유형으로 POWER()캐스팅 float_expression됩니다 BIGINT.

따라서 10 38 의 결과를 보유하기에 충분히 크지 않은 캐스트로 SELECT POWER(10.0, 38);인해 오버 플로우 오류로 실패합니다 . 10.0NUMERIC(38, 1)10 38 은 소수점 이하 39 자리를 갖도록 확장하는 반면 소수점 이하 NUMERIC(38, 1)37 자리에 1을 더한 값을 저장할 수 있기 때문입니다. 따라서, 최대 값을 NUMERIC(38, 1)저장할 수는 10 37 - 0.1.

이러한 이해를 바탕으로 다음과 같이 또 다른 오버플로 실패를 해결할 수 있습니다.

SELECT POWER(1000000000, 3);    -- one billion

10 억 (첫 번째 예의 1 조와 달리 NUMERIC(38, 0))은에 적합 할 정도로 작습니다 INT. 그러나 제 3의 거듭 제곱으로 10 억을 올리면 INT오버플로 오류가 발생 하기에는 너무 큽니다 .

다른 여러 함수는 비슷한 동작을 나타내며 출력 유형은 입력에 따라 다릅니다.

  • 수학 함수 : POWER(), CEILING(), FLOOR(), RADIANS(), DEGREES(), 및ABS()
  • 시스템 기능표현 : NULLIF(), ISNULL(), COALESCE(), IIF(), CHOOSE(), 및 CASE표현
  • 산술 연산자 : 양쪽 모두 SELECT 2 * @MAX_INT;SELECT @MAX_SMALLINT + @MAX_SMALLINT;변수는 지정된 데이터 유형의 경우, 예를 들면, 산술 오버플 초래한다.

결론

이 특별한 경우에는 솔루션을 사용하는 것 SELECT POWER(1e1, precision)...입니다. 이 때문에 모든 가능한 정밀도를 위해 작동 1e1으로 캐스팅됩니다 FLOAT저장할 수있는, 엄청나게 많은 수의 .

이러한 함수는 매우 일반적이므로 결과가 반올림되거나 동작으로 인해 오버플로 오류가 발생할 수 있음을 이해해야합니다. 출력에 특정 데이터 유형을 예상하거나 의존하는 경우 필요에 따라 관련 입력을 명시 적으로 캐스트하십시오.

아이들은 이제 이것을 알고 나면 번영 할 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.