Mengapa "SELECT POWER (10.0, 38.0);" melempar kesalahan aritmatika overflow?

15

Saya memperbarui saya IDENTITYnaskah cek meluap ke akun untuk DECIMALdan NUMERIC IDENTITYkolom .

Sebagai bagian dari pemeriksaan saya menghitung ukuran rentang tipe data untuk setiap IDENTITYkolom; Saya menggunakannya untuk menghitung berapa persentase rentang yang telah habis. Untuk DECIMALdan NUMERIC ukuran kisaran itu adalah di2 * 10^p - 2 mana pketepatannya.

Saya membuat banyak tabel uji dengan DECIMALdan NUMERIC IDENTITYkolom dan berusaha menghitung rentangnya sebagai berikut:

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

Ini melempar kesalahan berikut:

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

Saya mempersempitnya ke IDENTITYkolom tipe DECIMAL(38, 0)(yaitu dengan presisi maksimum), jadi saya kemudian mencoba POWER()perhitungan langsung pada nilai itu.

Semua pertanyaan berikut

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

juga menghasilkan kesalahan yang sama.

  • Mengapa SQL Server mencoba untuk mengkonversi output POWER(), yang bertipe FLOAT, ke NUMERIC(terutama ketika FLOATmemiliki prioritas lebih tinggi )?
  • Bagaimana cara menghitung rentang DECIMALatau NUMERICkolom secara dinamis untuk semua kemungkinan precision (termasuk p = 38, tentu saja)?
Nick Chammas
sumber

Jawaban:

18

Dari POWERdokumentasi :

Sintaksis

POWER ( float_expression , y )

Argumen

float_expression
Adalah ekspresi dari tipe float atau tipe yang secara implisit dapat dikonversi menjadi float .

y
Adalah kekuatan untuk meningkatkan float_expression . y dapat berupa ekspresi dari kategori tipe data numerik yang tepat atau perkiraan, kecuali untuk tipe data bit .

Jenis pengembalian

Mengembalikan jenis yang sama seperti yang dikirimkan dalam float_expression . Misalnya, jika desimal (2,0) dikirimkan sebagai float_expression, hasilnya yang dikembalikan adalah desimal (2,0).


Masukan pertama secara implisit dilemparkan ke floatjika perlu.

Perhitungan internal dilakukan dengan menggunakan floataritmatika dengan fungsi standar C Runtime Library (CRT)pow .

The floatOutput dari powkemudian dilemparkan kembali ke jenis tangan kiri operan (tersirat untuk menjadinumeric(3,1) ketika Anda menggunakan nilai literal 10,0).

Menggunakan eksplisit floatberfungsi dengan baik dalam kasus Anda:

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

Hasil yang tepat untuk 10 38 tidak dapat disimpan dalam SQL Server decimal/numerickarena akan membutuhkan 39 digit presisi (1 diikuti oleh 38 nol). Ketepatan maksimum adalah 38.

Martin Smith
sumber
23

Alih-alih mencampuri jawaban Martin lebih jauh, saya akan menambahkan sisa temuan saya mengenai POWER() sini.

Pegang celana Anda.

Pembukaan

Pertama, saya sajikan kepada Anda pameran A, dokumentasi MSDN untukPOWER() :

Sintaksis

POWER ( float_expression , y )

Argumen

float_expression Adalah ekspresi tipe float atau tipe yang dapat secara implisit dikonversi menjadi float.

Jenis pengembalian

Sama seperti float_expression.

Anda dapat menyimpulkan dari membaca baris terakhir yang POWER()merupakan tipe pengembaliannya FLOAT, tetapi baca lagi. float_expressionadalah "tipe float atau tipe yang secara implisit dapat dikonversi menjadi float". Jadi, terlepas dari namanya, float_expressionmungkin sebenarnya a FLOAT, a DECIMAL, atau a INT. Karena output dari POWER()adalah sama dengan float_expression, juga mungkin juga salah satu dari jenis mereka.

Jadi kami memiliki fungsi skalar dengan tipe pengembalian yang bergantung pada input. Mungkinkah?

Pengamatan

Saya mempersembahkan kepada Anda pameran B, sebuah tes yang menunjukkan bahwa POWER()keluarannya untuk tipe data yang berbeda tergantung pada inputnya .

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;

Hasil yang relevan adalah:

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

Apa yang tampaknya terjadi adalah yang POWER()dilemparkan float_expressionke dalam tipe terkecil yang cocok, tidak termasuk BIGINT.

Oleh karena itu, SELECT POWER(10.0, 38);gagal dengan kesalahan limpahan karena 10.0dilemparkan ke NUMERIC(38, 1)yang tidak cukup besar untuk menampung hasil 10 38 . Itu karena 10 38 mengembang untuk mengambil 39 digit sebelum desimal, sedangkan NUMERIC(38, 1)dapat menyimpan 37 digit sebelum desimal plus satu setelahnya. Oleh karena itu, nilai maksimum yang NUMERIC(38, 1)dapat disimpan adalah 10 37 - 0,1.

Berbekal pemahaman ini saya dapat menyusun kegagalan melimpah lainnya sebagai berikut.

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

Satu miliar (berlawanan dengan satu triliun dari contoh pertama, yang dilemparkan ke NUMERIC(38, 0)) hanya cukup kecil untuk ditampung dalam INT. Namun, satu miliar dinaikkan menjadi kekuatan ketiga, terlalu besar untuk ituINT , karenanya kesalahan melimpah.

Beberapa fungsi lain menunjukkan perilaku yang serupa, di mana tipe keluarannya bergantung pada inputnya:

  • Fungsi matematika : POWER(), CEILING(), FLOOR(), RADIANS(), DEGREES(), danABS()
  • Fungsi sistem dan ekspresi : NULLIF(), ISNULL(), COALESCE(), IIF(), CHOOSE(), dan CASEekspresi
  • Operator aritmatika : Baik SELECT 2 * @MAX_INT;dan SELECT @MAX_SMALLINT + @MAX_SMALLINT;, misalnya, menghasilkan aritmatika meluap ketika variabel dari tipe data yang disebut.

Kesimpulan

Dalam kasus khusus ini, solusinya adalah menggunakan SELECT POWER(1e1, precision).... Ini akan bekerja untuk semua kemungkinan precisioning sejak 1e1dilemparkan ke FLOAT, yang dapat menyimpan jumlah yang sangat besar .

Karena fungsi-fungsi ini sangat biasa, penting untuk dipahami bahwa hasil Anda mungkin bulat atau dapat menyebabkan kesalahan melimpah karena perilaku mereka. Jika Anda mengharapkan atau mengandalkan tipe data spesifik untuk output Anda, berikan input yang relevan secara eksplisit jika diperlukan.

Jadi anak-anak, sekarang Anda tahu ini, Anda mungkin maju dan makmur.

Nick Chammas
sumber