“SELECT POWER (10.0, 38.0);” neden aritmetik bir taşma hatası veriyor?


15

Taşma denetimi komut dosyamıIDENTITY hesaplamak DECIMALve NUMERIC IDENTITYsütunlar için güncelliyorum .

Kontrolün bir parçası olarak, her IDENTITYsütun için veri türünün aralığının boyutunu hesaplıyorum ; Bunu, bu aralığın yüzde kaçının tükendiğini hesaplamak için kullanıyorum. Çünkü DECIMALve NUMERIC bu aralığın büyüklüğü2 * 10^p - 2p hassasiyetin nerede olduğu.

Bir grup test tablosu DECIMALve NUMERIC IDENTITYsütunlar oluşturdum ve aralıklarını aşağıdaki gibi hesaplamaya çalıştım :

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

Bu, aşağıdaki hatayı attı:

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

Bunu IDENTITYtür sütunlarına daralttım DECIMAL(38, 0)(yani maksimum hassasiyetle), bu yüzden POWER()hesaplamayı doğrudan bu değer üzerinde denedim .

Aşağıdaki sorguların tümü

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

aynı hatayla sonuçlandı.

  • Neden SQL Server çıkışını dönüştürmek için deneyin yok POWER()tiptedir, FLOATiçin, NUMERIC(özellikle FLOATyüksek bir önceliğe sahiptir )?
  • Tüm olası kesinlikler için ( elbette, dahil ) bir DECIMALveya NUMERICsütunun aralığını dinamik olarak nasıl hesaplayabilirim p = 38?

Yanıtlar:


18

Gönderen POWERbelgeler :

Sözdizimi

POWER ( float_expression , y )

Argümanlar

float_expression
bir mi ifade tipi şamandıra veya örtük dönüştürülebilir bir türde yüzer .

y float_expression
değerinin yükseltileceği güçtür . y , bit veri türü hariç, tam sayısal veya yaklaşık sayısal veri türü kategorisinin bir ifadesi olabilir .

Dönüş Türleri

Float_expression öğesinde gönderilenle aynı türü döndürür . Bir Örneğin, ondalık (2,0) float_expression olarak gönderilirse, döndürülen sonuç ondalık (2,0).


İlk girdi, floatgerekirse dolaylı olarak yayınlanır .

Dahili hesaplama, floatstandart C Çalışma Zamanı Kütüphanesi (CRT) işlevi tarafından aritmetik kullanılarak gerçekleştirilir pow.

Daha sonra floatçıktı powsol soldaki işlenenin türüne geri döndürülür ( numeric(3,1)10.0 değişmez değerini kullandığınızda ima edilir ).

floatSizin durumunuzda açık bir işe yarar:

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

10 38 için kesin sonuç decimal/numeric39 basamak hassasiyet gerektireceği için bir SQL Server'da saklanamaz (1 ve ardından 38 sıfır). Maksimum hassasiyet 38'dir.


23

Martin'in yanıtıyla daha fazla uğraşmak yerine, POWER()buraya ilişkin diğer bulgularımı ekleyeceğim .

Pantolonlarınıza tutun.

önsöz

İlk olarak, size şu MSDN belgeleri olanPOWER() A'yı sergiliyorum :

Sözdizimi

POWER ( float_expression , y )

Argümanlar

float_expression Şamandıra türünün veya dolaylı olarak şamandıra dönüştürülebilen türün bir ifadesidir.

Dönüş Türleri

İle aynı float_expression.

POWER()Dönüş türünün son satırının okunmasıyla sonuçlanabilir FLOAT, ancak tekrar okunur. float_expression"şamandıra türünden veya dolaylı olarak şamandıra dönüştürülebilen türden" dir. Yani, ismine rağmen, float_expressionaslında a FLOAT, a DECIMALveya bir olabilir INT. Çıktısı ile POWER()aynı olduğundan float_expression, bu türlerden biri de olabilir.

Bu nedenle, girdiye bağlı dönüş türlerine sahip bir skaler fonksiyonumuz var. Olabilir mi?

Gözlemler

Size POWER(), girdisine bağlı olarak çıkışını farklı veri türlerine aktardığını gösteren bir test olan B'yi sergiliyorum .

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;

İlgili sonuçlar:

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

Görünen şey, buna dahil olmayan, ona uyan en küçük tipe POWER()dökülmesidir .float_expressionBIGINT

Bu nedenle, SELECT POWER(10.0, 38);çünkü bir taşma hatası ile başarısız 10.0döküm yapılmasını alır NUMERIC(38, 1)10 Sonuç alacak yeterince büyük olmadığı 38 . Çünkü 10 38 ondalık basamaktan önce 39 basamak alacak şekilde genişler, oysa ondalık NUMERIC(38, 1)basamaktan önce 37 basamak artı ondan sonra bir basamak depolayabilir. Bu nedenle, NUMERIC(38, 1)tutabileceği maksimum değer 10 37 - 0.1'dir.

Bu anlayışla donanmış bir taşma arızasını aşağıdaki gibi düzenleyebilirim.

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

Bir milyar (ilk örnekteki bir trilyonun aksine, kullanılan NUMERIC(38, 0)), bir sığacak kadar küçüktür INT. Bununla birlikte, üçüncü güce yükseltilen bir milyar INT, bu nedenle taşma hatası için çok büyük .

Diğer bazı işlevler, çıktı türlerinin girdilerine bağlı olduğu benzer davranış gösterir:

Sonuç

Bu özel durumda, çözüm kullanmaktır SELECT POWER(1e1, precision).... Bu yana tüm olası hassasiyetleri için çalışacaktır 1e1için döküm alır FLOATtutabilir, gülünç sayıda .

Bu işlevler çok yaygın olduğundan, sonuçlarınızın yuvarlanabileceğini veya davranışları nedeniyle taşma hatalarına neden olabileceğini anlamak önemlidir. Çıktınız için belirli bir veri türünü bekliyorsanız veya ona güveniyorsanız, ilgili girişi açıkça gerektiği gibi yayınlayın.

Yani çocuklar, şimdi bunu bildiğinize göre, ileri gidip gelişebilirsiniz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.