PDA

View Full Version : مشکل با خروجی حاصل ضرب اعداد بزرگ



pezhvakco
چهارشنبه 20 بهمن 1389, 17:48 عصر
جدول با دو ستون به نام های زیر است :
Col1 از نوع Real
Col2 از نوع Money

حالا اگه برای مثال مقدار 5050 به ستون اول (Col1) و مقدار 60100 به ستون دوم (Col2) بدهم و دستور زیر رو اجرا نماییم، مقدار خروجی مخالف با مقداری است که باید باشد :

SELECT Col1 * Col2 AS Expr1, ROUND(Col1 * Col2, 0) AS Expr2
FROM Table1خروجی کد دستور بالا :

Expr1 = 3.03505E+08
Exor2 = 303504992


5050 * 60100 = 303505000

تا جایی که من فهمیدم مشکل به این دلیل است که مقدار حاصل ضرب را به صورت نماد علمی نمایش میده .

پرسش : آیا میشه کاری کرد که خروجی حاصل ضرب به صورت نماد علمی نباشد یا خروجی تابع رند کردن همان حاصل ضرب باشد .

" پایگاه داده => Sql 2000 "

m_omrani
چهارشنبه 20 بهمن 1389, 22:47 عصر
به نظرم بهترین راه اینه که یک تابع SQLCLR مثلاً با استفاده از C# بنویسید که این کار رو انجام بده.

توجه داشته باشید ضرب اعداد بسیار بزرگ مشکل SQL Server نیست، مشکل خود CPU و محدودیت پشتیبانی زبان برنامه نویسی از دیتا تایپ ها است (که البته مشکل هم محسوب نمی شه، چون به هر حال هر چیزی محدودیتی داره، مثل پهنای باس، اندازه رم، سرعت سی پی یو، ...).

به عنوان مثال فرض کنید بخواهیم عملیاتی رو روی اعداد چند هزار رقمی انجام بدیم. نه زبان برنامه نویسی عادی (مثل همه زبان های برنامه نویسی مرسوم نظیر C#، Java، Delphi و ...) وجود داره که چنین دیتا تایپی داشته باشه و نه سرور عادی ای وجود داره که بتونید مثلاً ازش فضا اجاره کنید و مثلاً سایت یا برنامه ای رو روش راه اندازی کنید.

در چنین مواردی معمولاً کاری که انجام می شه اینه که دو عدد بسیار بزرگ رو به صورت رشته تعریف می کنن، بعد الگوریتم عملیات ریاضی مورد نظر رو (مثلاً ضرب) دقیقاً پیاده سازی می کنن.

مثلاً برای ضرب به صورت خیلی ساده می تونید دقیقاً همون عملیات ضربی رو که خودتون اگه بودید به طور دستی انجام می دادید پیاده سازی کنید، یعنی کاراکتر به کاراکتر از دو عدد یا دو رشته بخونید و اونها رو به مثلاً TinyInt تبدیل کنید، بعد در هم ضرب کنید، رقم یکان نتیجه رو به خروجی اضافه کنید و رقم دهگان رو در قسمت «ده بر یک» حفظ کنید و به ضرب ارقام بعدی اضافه کنید.

حاصل کار هم می شه یک رشته که نتیجه ضرب رو در خودش نگهداری می کنه.

البته دقت کنید این الگوریتم بسیار ساده است و تابع پیچیدگی زمانی اش n^2 است، لذا می تونه با زیاد شدن ارقام کُند بشه، اما الگوریتم های کاراتر دیگه ای وجود داره که می تونه همین کار رو براتون انجام بده.

توی اینترنت اگه سرچ کنید می تونید الگوریتم عملیات ریاضی اعداد بزرگ رو پیدا کنید. مثلاً از کلمات کلیدی زیر استفاده کنید:

big numbers math algorithms

محض یادآوری همون طور که گفتم صرفنظر از این که الگوریتم چی باشه، در نهایت بهترین راه برای جواب شما اینه که الگوریتم رو به شکل یک تابع SQLCLR پیاده سازی کنید و برای کارتون ازش استفاده کنید.

pezhvakco
پنج شنبه 21 بهمن 1389, 08:51 صبح
محض یادآوری همون طور که گفتم صرفنظر از این که الگوریتم چی باشه، در نهایت بهترین راه برای جواب شما اینه که الگوریتم رو به شکل یک تابع SQLCLR پیاده سازی کنید و برای کارتون ازش استفاده کنید.

اگه میشد یه کم بیش تر در مورد این تابع بگین، خوب میشد .

حاصل ضرب مورد نظر من خیلی عدد بزرگی نیست :

5050 * 60100 = 303,505,000 سیصدو سه میلیون و ...

چطور میشه کاری کرد نمایش به صورت نماد عملی رو بیخال بشه و یا خروجی تابع رند کردن (Round) درست باشه .

hossein_h62
پنج شنبه 21 بهمن 1389, 09:42 صبح
سلام
جناب pezhvakco (http://barnamenevis.org/member.php?u=59994) اون فیلد real که دارین چه ارقامی درش ذخیره میشن ؟ شاید از نوع مناسب تری میشد استفاده کرد.
ولی میشه به عدد مطلوبتون رسید :

select round(convert(numeric ,col1*col2),-3) as mul from table1

m_omrani
پنج شنبه 21 بهمن 1389, 09:47 صبح
اگه اعدادتون خیلی بزرگ نیست شاید اصلاً نیازی به چیزی که گفتم (ایجاد تابعی برای انجام عملیات ضرب) نیازی نباشه و بتونین از دیتا تایپ BigInt استفاده کنین. من دقیقاً نمی دونم در حالتی که به طور صریح در یک عبارت محاسباتی، وقتی دیتا تایپ عملوندها دقیقاً مشخص نباشه SQL Server از چه دیتا تایپ پیش فرضی استفاده می کنه.

به عنوان مثال در برخی زبان های برنامه نویسی از دیتا تایپ پیش فرض Double استفاده می شه، به همین خاطر نتیجه محاسبات از یک حدی که بیشتر بشه به صورت نماد علمی در میاد.

اگه این مساله در SQL Server هم صادق باشه و دیتا تایپ پیش فرض محاسبات ریاضی اش Double باشه برای جلوگیری از نمایش نماد علمی، باید عملوندهاتون رو (مثلاً با تابع CAST) به دیتا تایپ بزرگ تری مثل BigInt تبدیل کنید (به این شرط که نتیجه محاسبه تون اعشار نداشته باشه، مثلاً تقسیم نباشه، مثل حالت شما، ضرب باشه).

مثلاً این طوری:


select cast(5050 as bigint) * cast(60100 as bigint)


پی نوشت:
راستی نتیجه ضرب دو عددی که شما مثال زدید (5050 و 60100) بدون تبدیل به BigInt هم به صورت نماد علمی در نمیادها.

pezhvakco
پنج شنبه 21 بهمن 1389, 18:08 عصر
اون فیلد real که دارین چه ارقامی درش ذخیره میشن ؟ شاید از نوع مناسب تری میشد استفاده کرد.
ولی میشه به عدد مطلوبتون رسید :

select round(convert(numeric ,col1*col2),-3) as mul from table1

روش شما کار کرد.

اون ستون باید از نوع Real باشه چون میشه کاربر داده با اعشار هم وارد کند .


راستی نتیجه ضرب دو عددی که شما مثال زدید (5050 و 60100) بدون تبدیل به BigInt هم به صورت نماد علمی در نمیادها.

شما خروجی اون رو با تابع رند کردن گرفتین . اگه گرفتین و مشکلی نداشت شاید تنظیمات مربوط به این قسمت داره .

pezhvakco
شنبه 23 بهمن 1389, 11:03 صبح
کسی از دوستان روش دیگه ای نداره ؟


ولی میشه به عدد مطلوبتون رسید :

select round(convert(numeric ,col1*col2),-3) as mul from table1

این روش برای همین اعداد کار می کنه ولی اگه برای دو عدد دیگه امتحان، بشه نادرست کار میکنه :


Col1 = 500
Col2 = 21321

=>

SELECT ROUND(Col1 * Col2, 0) AS Expr1, ROUND(Col1 * Col2, - 3) AS Expr2
FROM Table1

ٍExpr1 = 10660500
Expr2 = 10661000

500 * 21321 = 10660500

hossein_h62
شنبه 23 بهمن 1389, 12:01 عصر
من فکر میکنم اگه اون فیلد real که دارین رو به نوع Numeric تبدیل کنید بهتر باشه.تابع round رو هم به میزانی که میخواین حاصل رو گرد کنه مقداردهی کنید.

pezhvakco
شنبه 23 بهمن 1389, 20:18 عصر
من فکر میکنم اگه اون فیلد real که دارین رو به نوع Numeric تبدیل کنید بهتر باشه.
برای داده های اعشار چکار کنم .

سپاس گذار از توجه ...

hossein_h62
شنبه 23 بهمن 1389, 21:09 عصر
برای داده های اعشار چکار کنم .
مثلا :


Data Type : Numeric
Precision : 18
Scale : 3

مقدار Scale تعداد ارقام اعشاری رو مشخص میکنه.