TSQL पावर (2., 64) के लिए गलत मान क्यों लौटाता है?


14

select POWER(2.,64.)के 18446744073709552000बदले देता है 18446744073709551616। ऐसा लगता है कि परिशुद्धता के केवल 16 अंक हैं (17 वें गोलाई में)।

यहां तक ​​कि परिशुद्धता को स्पष्ट करते हुए select power(cast(2 as numeric(38,0)),cast(64 as numeric(38,0)))यह अभी भी गोल परिणाम देता है।

ऐसा लगता है कि यह इस तरह की परिशुद्धता के 16 अंकों पर मनमाने ढंग से बाहर निकलने के लिए एक सुंदर बुनियादी ऑपरेशन की तरह है। उच्चतम यह सही ढंग से गणना कर सकता है केवल POWER(2.,56.), के लिए असफल POWER(2.,57.)। यहाँ क्या हो रहा है?

जो वास्तव में भयानक है वह select 2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.*2.;वास्तव में सही मूल्य देता है। इतनी चंचलता के लिए।


जवाबों:


17

से ऑनलाइन प्रलेखन :

POWER ( float_expression , y )  

तर्क

float_expression एक प्रकार की नाव या एक प्रकार की अभिव्यक्ति है जिसे अस्थायी रूप से नाव में परिवर्तित किया जा सकता है

निहितार्थ यह है कि जो भी आप पहले पैरामीटर के रूप में पास करते हैं, वह फ़ंक्शन निष्पादित float(53) होने से पहले अंतर्निहित रूप से डाली जाने वाली है। हालाँकि, यह (हमेशा?) मामला नहीं है

यदि ऐसा होता, तो यह सटीकता के नुकसान की व्याख्या करता:

फ़्लोट वैल्यू का रूपांतरण जो वैज्ञानिक संकेतन को दशमलव या संख्यात्मक का उपयोग करता है, केवल सटीक 17 अंकों के मूल्यों तक सीमित है। 17 राउंड से अधिक के साथ कोई भी मूल्य शून्य से अधिक है।

दूसरी ओर, शाब्दिक 2.प्रकार है numeric:

DECLARE @foo sql_variant;
SELECT @foo = 2.;
SELECT SQL_VARIANT_PROPERTY(@foo, 'BaseType');
GO
| (कोई कॉलम नाम नहीं) |
| : --------------- |
| सांख्यिक |

यहाँ dbfiddle

... और बहु ​​ऑपरेटर उच्च पूर्वता के साथ तर्क का डेटा प्रकार लौटाता है

ऐसा प्रतीत होता है कि 2016 (SP1) पर, सभी परिशुद्धता को बरकरार रखा गया है:

SELECT @@version;
GO
| (कोई कॉलम नाम नहीं) |
| : ------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ------- |
| Microsoft SQL सर्वर 2016 (SP1) (KB3182545) - 13.0.4001.0 (X64) <br> अक्टूबर 28 2016 18:17:30 <br> कॉपीराइट (c) Microsoft निगम <br> एक्सप्रेस संस्करण (64-बिट) Windows सर्वर पर 2012 R2 मानक 6.3 <X64> (बिल्ड 9600:) (हाइपरविजर) <br> |
SELECT POWER(2.,64.);
GO
| (कोई कॉलम नाम नहीं) |
| : ------------------- |
| 18446744073709551616 |

यहाँ dbfiddle

… लेकिन 2014 (SP2) पर, वे नहीं हैं:

SELECT @@version;
GO
| (कोई कॉलम नाम नहीं) |
| : ------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ------------------------------------ |
| Microsoft SQL Server 2014 (SP2) (KB3171021) - 12.0.5000.0 (X64) <br> जून 17 2016 19:14:09 <br> कॉपीराइट (c) Microsoft निगम <br> एक्सप्रेस संस्करण (64-बिट) Windows NT पर 6.3 <X64> (बिल्ड 9600:) (हाइपरविजर) <br> |
SELECT POWER(2.,64.);
GO
| (कोई कॉलम नाम नहीं) |
| : ------------------- |
| 18446744073709552000 |

यहाँ dbfiddle


1
इसलिए मूल रूप से, पॉवर फंक्शन सटीक के 17 अंकों से अधिक के लिए बेकार है। इसलिए यह सही परिणाम पैदा करता है, POWER(2.,56.) = 72057594037927936लेकिन इससे अधिक नहीं। मुझे लगता है कि मुझे अपना पॉवर फंक्शन लिखना होगा जो सिर्फ एक लूप, लोल में गुणा करता है।
त्रिनको

14

2 64 का परिणाम float(और realउस मामले के लिए) में बिल्कुल प्रतिनिधित्व योग्य है ।

समस्या तब उत्पन्न होती है जब यह सटीक परिणाम वापस numeric(पहले POWERऑपरेंड के प्रकार ) में परिवर्तित हो जाता है ।

डेटाबेस संगतता स्तर 130 शुरू होने से पहले, SQL सर्वर अधिकतम 17 अंकों के लिए रूपांतरण को गोल floatकर दिया numeric

रूपांतरण स्तर 130 के तहत, रूपांतरण के दौरान यथासंभव सटीक रूप से संरक्षित किया जाता है। यह नॉलेज बेस लेख में प्रलेखित है:

SQL सर्वर 2016 कुछ डेटा प्रकार और असामान्य संचालन को संभालने में सुधार करता है

Azure SQL डेटाबेस में इसका लाभ उठाने के लिए, आपको COMPATIBILITY_LEVEL130 पर सेट करना होगा:

ALTER DATABASE CURRENT SET COMPATIBILITY_LEVEL = 130;

वर्कलोड परीक्षण की आवश्यकता है क्योंकि नई व्यवस्था रामबाण नहीं है। उदाहरण के लिए:

SELECT POWER(10., 38);

... एक त्रुटि फेंकना चाहिए क्योंकि 10 38 में संग्रहीत नहीं किया जा सकता numeric(अधिकतम सटीक 38)। ओवरफ़्लो त्रुटि 120 संगतता के तहत परिणाम देती है, लेकिन 130 के तहत परिणाम है:

99999999999999997748809823456034029568 -- (38 digits)

2

थोड़ा सा गणित के साथ हम एक समाधान पा सकते हैं। विषम के लिए n:

2 ^ n 
= 2 ^ (2k + 1)
= 2 * (2 ^ 2k)
= 2 * (2 ^ k) * (2 ^ k)

यहां तक ​​कि n:

2 ^ n 
= 2 ^ (2k)
= 1 * (2 ^ 2k)
= 1 * (2 ^ k) * (2 ^ k)

T-SQL में लिखने का एक तरीका:

DECLARE @exponent INTEGER = 57;

SELECT (1 + @exponent % 2) * POWER(2., FLOOR(0.5 * @exponent)) * POWER(2., FLOOR(0.5 * @exponent));

SQL Server 2008 पर परीक्षण किया गया, परिणाम 144115188075855870 के बजाय 144115188075855872 है।

यह 113 के एक घातांक तक सभी तरह से काम करता है। ऐसा लगता है कि NUMERIC (38,0) 2 ^ 126 तक स्टोर कर सकता है, इसलिए काफी पूर्ण कवरेज नहीं है, लेकिन यदि आवश्यक हो तो सूत्र को और अधिक टुकड़ों में विभाजित किया जा सकता है ।


0

बस मनोरंजन के लिए, एक पुनरावर्ती CTE समाधान:

with 
  prm (p, e) as           -- parameters, to evaluate: p**e
    (select 2, 64),       -- (2 ** 64)  
  pow (power, exp) as 
    (select cast(p as numeric(30,0)), 
            e
     from prm 
     union all 
     select cast(power * power * (case when exp % 2 = 0 then 1 else p end) 
                 as numeric(30,0)), 
            exp / 2 
     from prm, pow 
     where exp > 1 
    ) 
select power 
from pow 
where exp = 1 ;
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.