10 ^ 37/1 গাণিতিক ওভারফ্লো ত্রুটি কেন ফেলে?


11

আমার প্রচুর সংখ্যক খেলোয়াড়ের সাথে সাম্প্রতিক প্রবণতা অব্যাহত রেখে আমি সম্প্রতি একটি ত্রুটি সিদ্ধ করেছিলাম যা আমি নিম্নলিখিত কোডটিতে চলে যাচ্ছি:

DECLARE @big_number DECIMAL(38,0) = '1' + REPLICATE(0, 37);

PRINT @big_number + 1;
PRINT @big_number - 1;
PRINT @big_number * 1;
PRINT @big_number / 1;

এই কোডটির জন্য আউটপুটটি আমি পাই:

10000000000000000000000000000000000001
9999999999999999999999999999999999999
10000000000000000000000000000000000000
Msg 8115, Level 16, State 2, Line 6
Arithmetic overflow error converting expression to data type numeric.

কি?

প্রথম 3 টি অপারেশন কেন শেষ হবে না? @big_numberস্পষ্টতই যদি আউটপুট সংরক্ষণ করতে পারে তবে কীভাবে একটি গাণিতিক ওভারফ্লো ত্রুটি থাকতে পারে @big_number / 1?

উত্তর:


18

পাটিগণিত অপারেশনের প্রসঙ্গে যথার্থতা এবং স্কেল বোঝা

আসুন এটি ভাঙ্গা যাক এবং বিভাজন পাটিগণিত অপারেটরের বিশদটি নিবিড়ভাবে দেখুনবিভাজন অপারেটরের ফলাফলের প্রকার সম্পর্কে এমএসডিএন এর এইটাই বলে :

ফলাফলের প্রকারগুলি

আর্গুমেন্টের ডেটা ধরণের উচ্চতর প্রাধান্য দিয়ে দেয়। আরও তথ্যের জন্য, ডেটা প্রকারের অগ্রাধিকার (লেনদেন-এসকিউএল) দেখুন

যদি একটি পূর্ণসংখ্যার লভ্যাংশকে একটি পূর্ণসংখ্যা বিভাজক দ্বারা ভাগ করা হয় তবে ফলাফলটি একটি পূর্ণসংখ্যা হয় যার ফলাফলের কোনও ভগ্নাংশ অংশ কেটে যায়।

আমরা জানি যে @big_numberএকটি DECIMAL। এসকিউএল সার্ভার কোন ডেটা টাইপ 1হিসাবে কাস্ট করে? এটি একটি কাস্ট INT। এর সাহায্যে আমরা এটি নিশ্চিত করতে পারি SQL_VARIANT_PROPERTY():

SELECT
      SQL_VARIANT_PROPERTY(1, 'BaseType')   AS [BaseType]  -- int
    , SQL_VARIANT_PROPERTY(1, 'Precision')  AS [Precision] -- 10
    , SQL_VARIANT_PROPERTY(1, 'Scale')      AS [Scale]     -- 0
;

কিক্সের জন্য, আমরা 1মূল কোড ব্লকের মধ্যে একটি স্পষ্টভাবে টাইপ করা মানের মতো প্রতিস্থাপন করতে পারি DECLARE @one INT = 1;এবং নিশ্চিত করতে পারি যে আমরা একই ফলাফল পেয়েছি।

সুতরাং আমরা একটি DECIMALএবং একটি আছে INT। যেহেতু DECIMALএর চেয়ে বেশি ডেটা টাইপের নজির রয়েছে তাই INTআমরা জানি আমাদের বিভাগের আউটপুট কেটে যাবে DECIMAL

তাহলে সমস্যা কোথায়?

সমস্যা DECIMALআউটপুটে স্কেলের সাথে । এখানে এসকিউএল সার্ভার পাটিগণিত ক্রিয়াকলাপ থেকে প্রাপ্ত ফলাফলের নির্ভুলতা এবং স্কেল কীভাবে নির্ধারণ করে সে সম্পর্কে নিয়মের একটি সারণী এখানে রয়েছে :

Operation                              Result precision                       Result scale *
-------------------------------------------------------------------------------------------------
e1 + e2                                max(s1, s2) + max(p1-s1, p2-s2) + 1    max(s1, s2)
e1 - e2                                max(s1, s2) + max(p1-s1, p2-s2) + 1    max(s1, s2)
e1 * e2                                p1 + p2 + 1                            s1 + s2
e1 / e2                                p1 - s1 + s2 + max(6, s1 + p2 + 1)     max(6, s1 + p2 + 1)
e1 { UNION | EXCEPT | INTERSECT } e2   max(s1, s2) + max(p1-s1, p2-s2)        max(s1, s2)
e1 % e2                                min(p1-s1, p2 -s2) + max( s1,s2 )      max(s1, s2)

* The result precision and scale have an absolute maximum of 38. When a result 
  precision is greater than 38, the corresponding scale is reduced to prevent the 
  integral part of a result from being truncated.

এবং এই টেবিলের ভেরিয়েবলগুলির জন্য আমাদের এখানে কী রয়েছে:

e1: @big_number, a DECIMAL(38, 0)
-> p1: 38
-> s1: 0

e2: 1, an INT
-> p2: 10
-> s2: 0

e1 / e2
-> Result precision: p1 - s1 + s2 + max(6, s1 + p2 + 1) = 38 + max(6, 11) = 49
-> Result scale:                    max(6, s1 + p2 + 1) =      max(6, 11) = 11

উপরের টেবিলটিতে নক্ষত্রের মন্তব্য অনুসারে, একটির সর্বাধিক নির্ভুলতা DECIMAL38 থাকতে পারে । সুতরাং আমাদের ফলাফলের নির্ভুলতা 49 থেকে 38 এ কেটে যায় এবং "ফলাফলের অবিচ্ছেদ্য অংশটি কেটে যাওয়া থেকে রক্ষা করতে সংশ্লিষ্ট স্কেল হ্রাস করা হয়।" স্কেল কীভাবে হ্রাস হবে তা এই মন্তব্য থেকে পরিষ্কার নয় , তবে আমরা এটি জানি:

সারণির সূত্র অনুসারে, দুটি গুলি ভাগ করার পরে আপনার ন্যূনতম সম্ভাব্য স্কেলটি DECIMAL6 থাকতে পারে।

সুতরাং, আমরা নিম্নলিখিত ফলাফল দিয়ে শেষ:

e1 / e2
-> Result precision: 49 -> reduced to 38
-> Result scale:     11 -> reduced to 6  

Note that 6 is the minimum possible scale it can be reduced to. 
It may be between 6 and 11 inclusive.

এটি কীভাবে গাণিতিক ওভারফ্লো ব্যাখ্যা করে

এখন উত্তর সুস্পষ্ট:

আমাদের বিভাগের আউটপুট কাস্ট হয়ে যায় DECIMAL(38, 6)এবং DECIMAL(38, 6)10 37 ধরে রাখতে পারে না ।

এর সাথে, আমরা আরও একটি বিভাগ তৈরি করতে পারি যা ফলাফলটি উপযুক্ত হতে পারে তা নিশ্চিত করে সফল হয় DECIMAL(38, 6):

DECLARE @big_number    DECIMAL(38,0) = '1' + REPLICATE(0, 37);
DECLARE @one_million   INT           = '1' + REPLICATE(0, 6);

PRINT @big_number / @one_million;

ফলাফল হলো:

10000000000000000000000000000000.000000

দশমিকের পরে 6 টি শূন্য নোট করুন। উপরের মতো DECIMAL(38, 6)ব্যবহার করে আমরা ফলাফলের ডেটা টাইপটি নিশ্চিত করতে পারি SQL_VARIANT_PROPERTY():

DECLARE @big_number   DECIMAL(38,0) = '1' + REPLICATE(0, 37);
DECLARE @one_million  INT           = '1' + REPLICATE(0, 6);

SELECT
      SQL_VARIANT_PROPERTY(@big_number / @one_million, 'BaseType')  AS [BaseType]  -- decimal
    , SQL_VARIANT_PROPERTY(@big_number / @one_million, 'Precision') AS [Precision] -- 38
    , SQL_VARIANT_PROPERTY(@big_number / @one_million, 'Scale')     AS [Scale]     -- 6
;

একটি বিপজ্জনক workaround

সুতরাং কিভাবে আমরা এই সীমাবদ্ধতা কাছাকাছি পেতে?

ঠিক আছে, এটি অবশ্যই নির্ভর করে আপনি কী জন্য এই গণনা তৈরি করছেন। একটি সমাধান যা আপনি অবিলম্বে ঝাঁপিয়ে যেতে পারেন তা হ'ল আপনার সংখ্যাগুলিকে FLOATগণনার জন্য রূপান্তর করা এবং তারপরে আপনি DECIMALযখন শেষ হয়ে যান তখন এগুলিকে আবার রূপান্তর করুন ।

এটি কিছু পরিস্থিতিতে কাজ করতে পারে তবে এই পরিস্থিতিতে কী তা বোঝার জন্য আপনার যত্নবান হওয়া উচিত। যেমনটি আমরা সবাই জানি, সংখ্যাগুলিতে এবং থেকে রূপান্তর FLOATকরা বিপজ্জনক এবং আপনাকে অপ্রত্যাশিত বা ভুল ফলাফল দিতে পারে।

আমাদের ক্ষেত্রে, 10 37 কে এবং এর থেকে রূপান্তর করা FLOATএমন একটি ফলাফল পায় যা কেবল সাধারণ ভুল :

DECLARE @big_number     DECIMAL(38,0)  = '1' + REPLICATE(0, 37);
DECLARE @big_number_f   FLOAT          = CAST(@big_number AS FLOAT);

SELECT
      @big_number                           AS big_number      -- 10^37
    , @big_number_f                         AS big_number_f    -- 10^37
    , CAST(@big_number_f AS DECIMAL(38, 0)) AS big_number_f_d  -- 9999999999999999.5 * 10^21
;

এবং সেখানে আপনি এটা আছে। আমার বাচ্চারা সাবধানে বিভক্ত।



2
আরই: "ক্লিনার ওয়ে"। আপনি হয়ত দেখতে চানSQL_VARIANT_PROPERTY
মার্টিন স্মিথ

@ মার্টিন - SQL_VARIANT_PROPERTYপ্রশ্নটিতে আলোচিত মত বিভাগগুলি সম্পাদন করতে আমি কীভাবে ব্যবহার করতে পারি তার একটি উদাহরণ বা দ্রুত ব্যাখ্যা সরবরাহ করতে পারেন ?
নিক চামাস

1
এখানে একটি উদাহরণ রয়েছে (ডেটাটাইপ নির্ধারণের জন্য একটি নতুন টেবিল তৈরির বিকল্প হিসাবে)
মার্টিন স্মিথ

@ মার্টিন - আহা হ্যাঁ, এটা অনেক পরিষ্কার!
নিক চামাস
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.