এক্সপ্রেরে কীভাবে ওভারফ্লো এড়ানো যায়। এ বি সি ডি


161

আমার মতামতটি A*B - C*Dএমনভাবে প্রকাশ করা উচিত যা দেখতে দেখতে: যেমন এর প্রকারগুলি: signed long long int A, B, C, D; প্রতিটি সংখ্যা সত্যই বড় হতে পারে (তার ধরণের উপচে পড়া নয়)। যদিও A*Bওভারফ্লো সৃষ্টি করতে পারে, একই সময় অভিব্যক্তি এ A*B - C*Dসত্যিই ছোট হতে পারে। আমি কীভাবে এটি সঠিকভাবে গণনা করতে পারি?

উদাহরণস্বরূপ:, MAX * MAX - (MAX - 1) * (MAX + 1) == 1কোথায় MAX = LLONG_MAX - nএবং এন - কিছু প্রাকৃতিক সংখ্যা।


17
নির্ভুলতা কতটা গুরুত্বপূর্ণ?
অনিরুধ রমনাথন

1
@ চথুলহু, দুর্দান্ত প্রশ্ন। সে সকলকে 10 বা কোনও কিছু দিয়ে ভাগ করে ছোট সংখ্যার সাহায্যে একটি সমতুল্য ফাংশন তৈরির চেষ্টা করতে পারে, তারপরে ফলাফলটি গুণ করে।
ক্রিস

4
ভারস এ, বি, সি, ডি স্বাক্ষরিত হয়েছে। এটি বোঝা যায় A - Cযে উপচে পড়তে পারে। এটি বিবেচনার জন্য একটি সমস্যা বা আপনি কি জানেন যে এটি আপনার ডেটা দিয়ে ঘটবে না?
উইলিয়াম মরিস

2
@MooingDuck কিন্তু আপনি পূর্বেই পরীক্ষা করতে পারবেন যদি অপারেশন ওভারফ্লো হবে stackoverflow.com/a/3224630/158285
bradgonesurfing

1
@ ক্রিস: না, আমি বলছি যে স্বাক্ষরিত ওভারফ্লো হয়েছে কিনা তা পরীক্ষা করার কোনও পোর্টেবল উপায় নেই। (ব্র্যাড আপনি portably সনাক্ত করতে পারে যে এটি সঠিক কিনা হবে ঘটতে)। ইনলাইন এসেম্বলি ব্যবহার করা চেক করার জন্য অনেকগুলি বহনযোগ্য ways
হাঁসকে মুভিং করা হচ্ছে

উত্তর:


120

আমার ধারণা এটি খুব তুচ্ছ মনে হচ্ছে। কিন্তু A*Bএক যে উপচে পড়া হতে পারে।

নির্ভুলতা না হারিয়ে আপনি নিম্নলিখিতটি করতে পারেন

A*B - C*D = A(D+E) - (A+F)D
          = AD + AE - AD - DF
          = AE - DF
             ^smaller quantities E & F

E = B - D (hence, far smaller than B)
F = C - A (hence, far smaller than C)

এই পচন আরও করা যেতে পারে ।
@ জিয়ান যেমন উল্লেখ করেছেন, এই টাইপটি দীর্ঘকাল স্বাক্ষর না করা থাকলে বিয়োগ বিয়োগের সময় যত্ন নেওয়া দরকার।


উদাহরণস্বরূপ, আপনার যে প্রশ্নটি রয়েছে তার ক্ষেত্রে এটি কেবল একটি পুনরাবৃত্তি লাগে,

 MAX * MAX - (MAX - 1) * (MAX + 1)
  A     B       C           D

E = B - D = -1
F = C - A = -1

AE - DF = {MAX * -1} - {(MAX + 1) * -1} = -MAX + MAX + 1 = 1

4
@ কালেব, ঠিক একই আলগোরিদিম প্রয়োগ করুনC*D
ক্রিস

2
আমি মনে করি আপনার ই বোঝাতে হবে যা ই প্রতিনিধিত্ব করে।
কালেব

7
দীর্ঘ দীর্ঘ এবং ডাবল উভয়ই 64 বিট। যেহেতু দ্বিগুণকে ব্যয়কারীর জন্য কিছু বিট বরাদ্দ করতে হয়, সুতরাং এটি নির্ভুলতা ক্ষতি ছাড়াই সম্ভাব্য মানগুলির একটি ছোট পরিসর রয়েছে।
জিম গ্যারিসন

3
@ চথুলহু - আমার কাছে মনে হয় এটি কেবল তখনই কাজ করবে যদি সমস্ত সংখ্যা খুব বেশি হয় ... উদাহরণস্বরূপ, আপনি এখনও {এ, বি, সি, ডি} = {ম্যাক্স, ম্যাক্স, ম্যাক্স, ২ with এর সাথে উপচে পড়বেন} ওপিতে বলা হয়েছে "প্রতিটি সংখ্যা সত্যই বড় হতে পারে", তবে সমস্যা বিবরণী থেকে এটি পরিষ্কার নয় যে প্রতিটি সংখ্যা অবশ্যই বড় হতে হবে be
কেভিন কে

4
কেউ যদি A,B,C,Dনেতিবাচক হয় তবে কি? না Eবা Fআরও বড় হতে হবে?
শুক্রবার

68

সবচেয়ে সহজ এবং সর্বাধিক সাধারণ সমাধানটি এমন একটি প্রতিনিধিত্ব ব্যবহার করা উচিত যা উপচে পড়তে পারে না, হয় দীর্ঘ পূর্ণসংখ্যার লাইব্রেরি ব্যবহার করে (যেমন: http://gmplib.org/ ) অথবা স্ট্রাক্ট বা অ্যারে ব্যবহার করে এবং এক ধরণের দীর্ঘ গুণ প্রয়োগ করে ( উদাহরণস্বরূপ, প্রতিটি সংখ্যা দুটি 32 বিট অর্ধে বিভক্ত করা এবং নীচের মত গুণ করা:

(R1 + R2 * 2^32 + R3 * 2^64 + R4 * 2^96) = R = A*B = (A1 + A2 * 2^32) * (B1 + B2 * 2^32) 
R1 = (A1*B1) % 2^32
R2 = ((A1*B1) / 2^32 + (A1*B2) % 2^32 + (A2*B1) % 2^32) % 2^32
R3 = (((A1*B1) / 2^32 + (A1*B2) % 2^32 + (A2*B1) % 2^32) / 2^32 + (A1*B2) / 2^32 + (A2*B1) / 2^32 + (A2*B2) % 2^32) %2^32
R4 = ((((A1*B1) / 2^32 + (A1*B2) % 2^32 + (A2*B1) % 2^32) / 2^32 + (A1*B2) / 2^32 + (A2*B1) / 2^32 + (A2*B2) % 2^32) / 2^32) + (A2*B2) / 2^32

শেষের ফলাফলটি b৪ বিটের মধ্যে খাপ খায় আপনার আসলে সত্যিকার অর্থে R3 এর বেশিরভাগ বিট এবং আর 4-র কোনওটির প্রয়োজন নেই


8
উপরের গণনাটি দেখতে যতটা জটিল তা দেখতে জটিল নয়, এটি সত্যিকারের বেস 2 base 32 তে দীর্ঘ দীর্ঘ গুণ এবং সি এর কোডটি আরও সহজ দেখানো উচিত। এছাড়াও, আপনার প্রোগ্রামটিতে এই কাজটি করার জন্য জেনেরিক ফাংশন তৈরি করা ভাল ধারণা হবে।
অফির

46

মনে রাখবেন যে এটি মোড়ক নয় কারণ এটি মোড়কের চারপাশে স্বাক্ষরিত ওভারফ্লোতে নির্ভর করে। (জিসিসিতে সংকলক পতাকা রয়েছে যা এটি সক্ষম করে))

তবে আপনি যদি কেবল সমস্ত গণনা এতে করে থাকেন long longতবে সরাসরি সূত্র প্রয়োগের ফলাফল:
(A * B - C * D)যতক্ষণ সঠিক ফলাফলটি এটিতে ফিট হয় ততক্ষণ সঠিক হবে long long


এখানে এমন একটি কাজ রয়েছে যা কেবল স্বাক্ষরিত পূর্ণসংখ্যার জন্য স্বাক্ষরযুক্ত পূর্ণসংখ্যাটি কাস্টিংয়ের বাস্তবায়ন-সংজ্ঞায়িত আচরণের উপর নির্ভর করে। তবে এটি আজ প্রায় প্রতিটি সিস্টেমে কাজ করার আশা করা যায়।

(long long)((unsigned long long)A * B - (unsigned long long)C * D)

এটি unsigned long longস্ট্যান্ডার্ডের মাধ্যমে ওভারফ্লো আচরণের মোড়কের আশেপাশের গ্যারান্টিযুক্ত ইনপুটগুলিকে কাস্ট করে । শেষে স্বাক্ষরিত পূর্ণসংখ্যায় ফিরে কাস্ট করা বাস্তবায়ন-সংজ্ঞায়িত অংশ, তবে আজ প্রায় সমস্ত পরিবেশে কাজ করবে।


আপনার যদি আরও পেডেন্টিক সমাধানের প্রয়োজন হয় তবে আমার মনে হয় আপনাকে "দীর্ঘ গাণিতিক" ব্যবহার করতে হবে


+1 আপনি একমাত্র এটি লক্ষ্য করেন। একমাত্র কৌতূহলপূর্ণ অংশ হ'ল মোড়কে চারপাশে ওভারফ্লো করতে সংকলকটি সেট করা হচ্ছে এবং সঠিক ফলাফলটি আসলে একটি এর সাথে খাপ খায় কিনা তা খতিয়ে দেখছে long long
রহস্যময়

2
এমনকি কোনও কৌশল ছাড়াই নির্দোষ সংস্করণ বেশিরভাগ বাস্তবায়নের ক্ষেত্রে সঠিক কাজ করবে ; এটি স্ট্যান্ডার্ড দ্বারা গ্যারান্টিযুক্ত নয়, তবে এটির ব্যর্থ হওয়ার জন্য আপনাকে একটি 1-এর পরিপূরক মেশিন বা অন্য কোনও বেশ অদ্ভুত যন্ত্র আবিষ্কার করতে হবে।
hobbs

1
আমি মনে করি এটি একটি গুরুত্বপূর্ণ উত্তর। আমি সম্মত হলাম বাস্তবায়ন নির্দিষ্ট আচরণ অনুমান করা সঠিক প্রোগ্রামিং নাও হতে পারে তবে প্রতিটি ইঞ্জিনিয়ারের মডুলো পাটিগণিত বুঝতে হবে এবং পারফরম্যান্স অপরিহার্য হলে সামঞ্জস্যপূর্ণ আচরণ নিশ্চিত করতে সঠিক সংকলক পতাকাগুলি কীভাবে পাবেন। ডিএসপি ইঞ্জিনিয়াররা স্থির পয়েন্ট ফিল্টার বাস্তবায়নের জন্য এই আচরণের উপর নির্ভর করে, যার জন্য গৃহীত উত্তরের অগ্রহণযোগ্য কর্মক্ষমতা থাকবে।
পিটার এম

18

এটি কাজ করা উচিত (আমি মনে করি):

signed long long int a = 0x7ffffffffffffffd;
signed long long int b = 0x7ffffffffffffffd;
signed long long int c = 0x7ffffffffffffffc;
signed long long int d = 0x7ffffffffffffffe;
signed long long int bd = b / d;
signed long long int bdmod = b % d;
signed long long int ca = c / a;
signed long long int camod = c % a;
signed long long int x = (bd - ca) * a * d - (camod * d - bdmod * a);

এখানে আমার ব্যয়:

x = a * b - c * d
x / (a * d) = (a * b - c * d) / (a * d)
x / (a * d) = b / d - c / a

now, the integer/mod stuff:
x / (a * d) = (b / d + ( b % d ) / d) - (c / a + ( c % a ) / a )
x / (a * d) = (b / d - c / a) - ( ( c % a ) / a - ( b % d ) / d)
x = (b / d - c / a) * a * d - ( ( c % a ) * d - ( b % d ) * a)

1
ধন্যবাদ @ ব্র্যাডগোনেসুরফিং - আপনি কি এমন একটি ইনপুট সরবরাহ করতে পারবেন? আমি আমার উত্তর আপডেট করেছি, এটি কার্যকর করেছি এবং এটি কাজ করে (বিডি এবং সিএ 0 হয়) ...
প্যাচেটপ

1
হুম। এখন আমি এটি সম্পর্কে না ভাবা সম্ভবত। ডি = 1 এবং এ = 1 এবং বি = ম্যাক্সিন্ট এবং সি = ম্যাক্সিন্টের সাথে কেসটি ডিজেনারেট করুন এটি এখনও কাজ করে। দুর্দান্ত :)
ব্র্যাডগোনসার্ফিং

1
@ পাসকেপ: এ = 1, বি = 0x7fffffffffffffff, সি = -0x7fffffffffffffff, ডি = 1 (নোট সি নেতিবাচক)। যদিও চালাক, আমি নিশ্চিত আপনার কোডটি সমস্ত ধনাত্মক সংখ্যা সঠিকভাবে পরিচালনা করে।
হাঁস মুভি

3
@ মুভিংডাক তবে আপনার সেটের চূড়ান্ত উত্তরটি পাশাপাশি উপচে পড়েছে সুতরাং এটি কোনও বৈধ সেটআপ নয়। এটি কেবল তখনই কাজ করে যদি প্রতিটি পক্ষ একই সংকেতের হয় ফলে ফলক বিয়োগফল সীমার মধ্যে থাকে।
bradgonesurfing

1
স্ট্যাক ওভারফ্লোতে অদ্ভুত কিছু আছে যখন এই উত্তরটি যা সবচেয়ে সহজ এবং সেরাটি সেরা রানের উত্তরের তুলনায় এত কম স্কোর পেয়েছে।
bradgonesurfing

9

আপনি আপনার সমস্ত মানগুলির জন্য একটি সর্বাধিক সাধারণ ফ্যাক্টর গণনা করতে পারেন এবং তারপরে আপনার গাণিতিক ক্রিয়াকলাপগুলি করার আগে সেটিকে ফ্যাক্টর দ্বারা ভাগ করে আবার গুন করে নিতে পারেন। এই অনুমান যে এই ধরনের একটি ফ্যাক্টর বিদ্যমান, তবে (উদাহরণস্বরূপ, যদি A, B,C এবং Dতুলনামূলকভাবে প্রধানমন্ত্রী হতে ঘটতে, তারা একটি সাধারণ ফ্যাক্টর হবে না)।

একইভাবে, আপনি লগ-স্কেলগুলিতে কাজ করতে বিবেচনা করতে পারেন তবে এটি সংখ্যার নির্ভুলতার সাপেক্ষে কিছুটা ভীতিজনক হতে চলেছে।


1
লোগারিদমিং long doubleপাওয়া গেলে ভাল মনে হয়। সেক্ষেত্রে নির্ভুলতার একটি গ্রহণযোগ্য স্তর অর্জন করা যেতে পারে (এবং ফলাফলের কেন্দ্রটি গোল করা হবে)।

9

যদি ফলাফলটি দীর্ঘ দীর্ঘ অন্তরে ফিট করে তবে A * বিসি * ডি এক্সপ্রেশনটি ঠিক আছে কারণ এটি পাটিগণিত মোড 2 ^ 64 সম্পাদন করে এবং সঠিক ফলাফল দেবে give সমস্যাটি দীর্ঘ ফলাফলের জন্য ফিট করে কিনা তা জানা। এটি সনাক্ত করতে, আপনি ডাবলস ব্যবহার করে নিম্নলিখিত কৌশলটি ব্যবহার করতে পারেন:

if( abs( (double)A*B - (double)C*D ) > MAX_LLONG ) 
    Overflow
else 
    return A*B-C*D;

এই পদ্ধতির সাথে সমস্যাটি হ'ল আপনি ডাবলসের ম্যান্টিসার যথাযথতা দ্বারা সীমাবদ্ধ (54 বিট?) তাই আপনাকে এ * বি এবং সি * ডি পণ্যগুলিকে সীমাবদ্ধ করতে হবে +৩ + ৫৪ বিট (বা সম্ভবত কিছুটা কম)।


এটি সবচেয়ে ব্যবহারিক উদাহরণ। সাফ এবং সঠিক উত্তর দেয় (বা ইনপুটগুলি খারাপ হলে একটি ব্যতিক্রম ছুঁড়ে দেয়)।
লাকাটা

1
সুন্দর এবং মার্জিত! অন্যরা যে ফাঁদে পড়েছিল তাতে আপনি পড়ে যাননি। আরও একটি জিনিস: আমি বাজি ধরবো এমন কয়েকটি উদাহরণ রয়েছে যেখানে ডাবল গণনাটি কেবল গোলাকার ত্রুটির কারণে MAX_LLONG এর নীচে রয়েছে। আমার গাণিতিক প্রবৃত্তি আমাকে বলেছে যে আপনি তার পরিবর্তে দ্বিগুণ এবং দীর্ঘ ফলাফলের পার্থক্য গণনা করুন এবং এটিকে MAX_LLONG / 2 বা অন্য কোনও কিছুর সাথে তুলনা করুন। এই পার্থক্যটি হ'ল ডাবল গণনা এবং প্লাস ওভারফ্রোলের বৃত্তাকার ত্রুটি এবং সাধারণত তুলনামূলকভাবে কম হওয়া উচিত তবে আমি যে ক্ষেত্রে উল্লেখ করেছি এটি বড় হবে। তবে এই মুহুর্তে আমি নিশ্চিতভাবে খুঁজে পেতে খুব অলস। :-)
হ্যান্স-পিটার স্টার

9
E = max(A,B,C,D)
A1 = A -E;
B1 = B -E;
C1 = C -E;
D1 = D -E;

তারপর

A*B - C*D = (A1+E)*(B1+E)-(C1+E)(D1+E) = (A1+B1-C1-D1)*E + A1*B1 -C1*D1

7

আপনি প্রতিটি সংখ্যাকে একটি অ্যারেতে লিখতে পারতেন, প্রতিটি উপাদান একটি ডিজিট এবং বহুবচন হিসাবে গণনাগুলি করতে পারতেন । ফলাফলযুক্ত বহুভুজটি নিন যা একটি অ্যারে, এবং অ্যারের প্রতিটি উপাদানকে 10 এর সাথে অ্যারের অবস্থানের শক্তিতে (প্রথম অবস্থানটি সবচেয়ে বড় এবং শেষটি শূন্য হচ্ছে) গুন করে ফলাফল গণনা করুন।

সংখ্যাটি 123প্রকাশ করা যেতে পারে:

123 = 100 * 1 + 10 * 2 + 3

যার জন্য আপনি কেবল একটি অ্যারে তৈরি করুন [1 2 3]

আপনি এ, বি, সি এবং ডি সমস্ত সংখ্যার জন্য এটি করেন এবং তারপরে আপনি এগুলি বহুবচন হিসাবে গুন করেন। ফলস্বরূপ বহুভিত্তিক উত্সাহটি পাওয়ার পরে আপনি কেবল এটি থেকে নম্বরটি পুনর্গঠন করুন।


2
কি তা জানি না তবে আমাকে খুঁজে বের করতে হবে। করা :)। আমি আমার বান্ধবীর সাথে শপিং করার সময় এটি আমার মাথার শীর্ষের একটি সমাধান :)
মিহাই

আপনি একটি বেস 10 অ্যারে bignums বাস্তবায়ন করছেন। জিএমপি হ'ল একটি মানের বিনাইনাম লাইব্রেরি যা বেস 4294967296 বেস ব্যবহার করে M অনেক বেশি দ্রুত। যদিও ডাউনওয়েট নেই, কারণ উত্তরটি সঠিক, এবং দরকারী।
হাঁস মুভি

ধন্যবাদ :)। এটি কার্যকরভাবে জানতে যে এটি করার এক উপায় তবে এর থেকে আরও ভাল উপায় রয়েছে তাই এটি এর মতো করবেন না। কমপক্ষে এই পরিস্থিতিতে নয় :)
মিহাই

যাইহোক ... এই সমাধানটি ব্যবহার করে আপনি যেকোন আদিম ধরণের (100 ডিজিটের নম্বর এর মতো) তুলনায় অনেক বড় সংখ্যক কম্পিউটার করতে পারবেন এবং ফলাফলটিকে অ্যারে হিসাবে রাখবেন। এটি একটি আপ ভোজনের যোগ্য: পি
মিহাই

আমি নিশ্চিত না যে এটি একটি উত্সাহ লাভ করে, যেহেতু এই পদ্ধতিটি (যদিও কার্যকর এবং তুলনামূলকভাবে সহজভাবে বোঝা যায়) স্মৃতি ক্ষুধার্ত এবং ধীর।
হাঁস মুভি

6

একটি যখন signed long long intধরে রাখবে না A*B, তাদের মধ্যে দুটি থাকবে। সুতরাং A*Bগাছের বিভিন্ন শর্তগুলিতে বিভিন্ন ক্ষতিকারকগুলির সাথে সংযুক্ত হতে পারে, এর মধ্যে যে কোনও একটিও মানানসই signed long long int

A1=A>>32;
A0=A & 0xffffffff;
B1=B>>32;
B0=B & 0xffffffff;

AB_0=A0*B0;
AB_1=A0*B1+A1*B0;
AB_2=A1*B1;

একই জন্য C*D

সোজা পথ অনুসরণ করে, বিয়োগ প্রতিটি জোড় AB_iএবং CD_iএকইভাবে প্রতিটি অতিরিক্ত অতিরিক্ত বিট (সঠিকভাবে 1-বিট পূর্ণসংখ্যার) ব্যবহার করে করা যেতে পারে। সুতরাং আমরা যদি E = A * BC * D বলি তবে আপনি এর মতো কিছু পান:

E_00=AB_0-CD_0 
E_01=(AB_0 > CD_0) == (AB_0 - CD_0 < 0) ? 0 : 1  // carry bit if overflow
E_10=AB_1-CD_1 
...

আমরা উপরের অংশে অর্ধেক স্থানান্তর করার দ্বারা অবিরত E_10করার E_20(32 দ্বারা শিফ্ট ও যোগ করুন, তারপর উপরের অর্ধেক নিশ্চিহ্ন E_10)।

এখন আপনি ক্যারি বিটটি E_11এটিকে ডান চিহ্ন (অ বহনকারী অংশ থেকে প্রাপ্ত) দিয়ে যুক্ত করে মুক্ত করতে পারেন E_20। যদি এটি একটি ওভারফ্লো ট্রিগার করে, ফলাফলটিও ফিট করে না।

E_10E_00 (শিফট, যোগ, মুছুন) এবং ক্যারি বিট থেকে উপরের অর্ধেকটি নিতে এখন পর্যাপ্ত 'স্পেস' রয়েছে E_01

E_10এখন আবার বড় হতে পারে, তাই আমরা স্থানান্তর পুনরাবৃত্তি E_20

এই মুহুর্তে, E_20অবশ্যই শূন্য হতে হবে, অন্যথায় ফলাফল ফিট হবে না। উপরের অর্ধেকটি E_10স্থানান্তরের ফলেও খালি রয়েছে।

চূড়ান্ত পদক্ষেপ হ'ল নীচের অর্ধেকটি আবার স্থানান্তর E_20করা E_10

যদি প্রত্যাশাটি হোল্ডগুলির E=A*B+C*Dসাথে খাপ signed long long intখায় তবে আমাদের এখন তা আছে

E_20=0
E_10=0
E_00=E

1
এটি আসলে সরলীকৃত সূত্র যা অফিরের গুণক সূত্রটি ব্যবহার করে এবং প্রতিটি অকেজো অস্থায়ী ফলাফল মুছে ফেললে একটি পেতে পারে।
ড্রোনাস

3

আপনি যদি জানেন যে চূড়ান্ত ফলাফলটি আপনার পূর্ণসংখ্যার প্রকারে প্রতিনিধিত্বযোগ্য, আপনি নীচের কোডটি ব্যবহার করে এই গণনাটি দ্রুত সম্পাদন করতে পারেন। যেহেতু সি স্ট্যান্ডার্ডটি নির্দিষ্ট করে যে স্বাক্ষরবিহীন গাণিতিকগুলি মডুলো গাণিতিক এবং উপচে পড়ে না তাই আপনি গণনা সম্পাদনের জন্য স্বাক্ষরবিহীন প্রকারটি ব্যবহার করতে পারেন।

নিম্নলিখিত কোডটি ধরে নিয়েছে যে একই প্রস্থের একটি স্বাক্ষরবিহীন প্রকার রয়েছে এবং স্বাক্ষরিত প্রকারটি মানগুলি উপস্থাপন করতে সমস্ত বিট নিদর্শন ব্যবহার করে (কোনও ফাঁদ উপস্থাপনা নয়, স্বাক্ষরযুক্ত প্রকারের সর্বনিম্ন স্বাক্ষরযুক্ত প্রকারের অর্ধেক মডিউলটির negativeণাত্মক)। যদি এটি কোনও সি বাস্তবায়নে না ধরে থাকে তবে তার জন্য কনভার্টটোসাইন্ট রুটিনে সাধারণ সামঞ্জস্য করা যেতে পারে।

নিম্নলিখিত ব্যবহারের signed charএবং unsigned charকোড প্রকট। আপনার বাস্তবায়ন জন্য সংজ্ঞা পরিবর্তন Signedকরার typedef signed long long int Signed;এবং সংজ্ঞা Unsignedথেকে typedef unsigned long long int Unsigned;

#include <limits.h>
#include <stdio.h>
#include <stdlib.h>


//  Define the signed and unsigned types we wish to use.
typedef signed char   Signed;
typedef unsigned char Unsigned;

//  uHalfModulus is half the modulus of the unsigned type.
static const Unsigned uHalfModulus = UCHAR_MAX/2+1;

//  sHalfModulus is the negation of half the modulus of the unsigned type.
static const Signed   sHalfModulus = -1 - (Signed) (UCHAR_MAX/2);


/*  Map the unsigned value to the signed value that is the same modulo the
    modulus of the unsigned type.  If the input x maps to a positive value, we
    simply return x.  If it maps to a negative value, we return x minus the
    modulus of the unsigned type.

    In most C implementations, this routine could simply be "return x;".
    However, this version uses several steps to convert x to a negative value
    so that overflow is avoided.
*/
static Signed ConvertToSigned(Unsigned x)
{
    /*  If x is representable in the signed type, return it.  (In some
        implementations, 
    */
    if (x < uHalfModulus)
        return x;

    /*  Otherwise, return x minus the modulus of the unsigned type, taking
        care not to overflow the signed type.
    */
    return (Signed) (x - uHalfModulus) - sHalfModulus;
}


/*  Calculate A*B - C*D given that the result is representable as a Signed
    value.
*/
static signed char Calculate(Signed A, Signed B, Signed C, Signed D)
{
    /*  Map signed values to unsigned values.  Positive values are unaltered.
        Negative values have the modulus of the unsigned type added.  Because
        we do modulo arithmetic below, adding the modulus does not change the
        final result.
    */
    Unsigned a = A;
    Unsigned b = B;
    Unsigned c = C;
    Unsigned d = D;

    //  Calculate with modulo arithmetic.
    Unsigned t = a*b - c*d;

    //  Map the unsigned value to the corresponding signed value.
    return ConvertToSigned(t);
}


int main()
{
    //  Test every combination of inputs for signed char.
    for (int A = SCHAR_MIN; A <= SCHAR_MAX; ++A)
    for (int B = SCHAR_MIN; B <= SCHAR_MAX; ++B)
    for (int C = SCHAR_MIN; C <= SCHAR_MAX; ++C)
    for (int D = SCHAR_MIN; D <= SCHAR_MAX; ++D)
    {
        //  Use int to calculate the expected result.
        int t0 = A*B - C*D;

        //  If the result is not representable in signed char, skip this case.
        if (t0 < SCHAR_MIN || SCHAR_MAX < t0)
            continue;

        //  Calculate the result with the sample code.
        int t1 = Calculate(A, B, C, D);

        //  Test the result for errors.
        if (t0 != t1)
        {
            printf("%d*%d - %d*%d = %d, but %d was returned.\n",
                A, B, C, D, t0, t1);
            exit(EXIT_FAILURE);
        }
    }
    return 0;
}

2

আপনি সমীকরণটিকে ছোট ছোট উপাদানগুলিতে ভাঙ্গতে চেষ্টা করতে পারেন যা উপচে পড়ে না।

AB - CD
= [ A(B - N) - C( D - M )] + [AN - CM]

= ( AK - CJ ) + ( AN - CM)

    where K = B - N
          J = D - M

যদি উপাদানগুলি এখনও উপচে পড়ে যায় তবে আপনি এগুলি পুনরাবৃত্তভাবে ছোট উপাদানগুলিতে বিভক্ত করতে পারেন এবং তারপরে পুনরুদ্ধার করতে পারেন।


এটি সঠিক বা নাও হতে পারে তবে এটি অবশ্যই বিভ্রান্তিকর। আপনি সংজ্ঞায়িত করেন Kএবং Jকেন Nএবং কেন না M। এছাড়াও, আমি মনে করি আপনি সমীকরণটিকে আরও বড় টুকরো টুকরো করছেন। আপনার জটিল (AK-CJ)(AB-CD)
পদক্ষেপটি

এন কিছু থেকে সরলীকৃত হয় না। এটিকে ছোট করার জন্য এটি এ থেকে বিয়োগ মাত্র একটি সংখ্যা। আসলে এটি প্যাকএইচপিটির অনুরূপ তবে নিকৃষ্ট সমাধান। এখানে আমি এটি আরও ছোট করতে পূর্ণসংখ্যা বিভাগের পরিবর্তে বিয়োগ ব্যবহার করছি।
ব্র্যাডগোনসার্ফিং

2

আমি সমস্ত প্রান্তের কেসগুলি কভার না করে থাকতে পারি না বা আমি কঠোরভাবে এটি পরীক্ষা করে দেখিনি তবে এটি এমন একটি কৌশল প্রয়োগ করে যা আমি ৮০ এর দশকে 16-বিট সিপিইউতে 32-বিট পূর্ণসংখ্যার গণিত করার সময় মনে করি using মূলত আপনি 32 বিটগুলিকে দুটি 16-বিট ইউনিটে বিভক্ত করেছেন এবং তাদের সাথে পৃথকভাবে কাজ করেন।

public class DoubleMaths {
  private static class SplitLong {
    // High half (or integral part).
    private final long h;
    // Low half.
    private final long l;
    // Split.
    private static final int SPLIT = (Long.SIZE / 2);

    // Make from an existing pair.
    private SplitLong(long h, long l) {
      // Let l overflow into h.
      this.h = h + (l >> SPLIT);
      this.l = l % (1l << SPLIT);
    }

    public SplitLong(long v) {
      h = v >> SPLIT;
      l = v % (1l << SPLIT);
    }

    public long longValue() {
      return (h << SPLIT) + l;
    }

    public SplitLong add ( SplitLong b ) {
      // TODO: Check for overflow.
      return new SplitLong ( longValue() + b.longValue() );
    }

    public SplitLong sub ( SplitLong b ) {
      // TODO: Check for overflow.
      return new SplitLong ( longValue() - b.longValue() );
    }

    public SplitLong mul ( SplitLong b ) {
      /*
       * e.g. 10 * 15 = 150
       * 
       * Divide 10 and 15 by 5
       * 
       * 2 * 3 = 5
       * 
       * Must therefore multiply up by 5 * 5 = 25
       * 
       * 5 * 25 = 150
       */
      long lbl = l * b.l;
      long hbh = h * b.h;
      long lbh = l * b.h;
      long hbl = h * b.l;
      return new SplitLong ( lbh + hbl, lbl + hbh );
    }

    @Override
    public String toString () {
      return Long.toHexString(h)+"|"+Long.toHexString(l);
    }
  }

  // I'll use long and int but this can apply just as easily to long-long and long.
  // The aim is to calculate A*B - C*D without overflow.
  static final long A = Long.MAX_VALUE;
  static final long B = Long.MAX_VALUE - 1;
  static final long C = Long.MAX_VALUE;
  static final long D = Long.MAX_VALUE - 2;

  public static void main(String[] args) throws InterruptedException {
    // First do it with BigIntegers to get what the result should be.
    BigInteger a = BigInteger.valueOf(A);
    BigInteger b = BigInteger.valueOf(B);
    BigInteger c = BigInteger.valueOf(C);
    BigInteger d = BigInteger.valueOf(D);
    BigInteger answer = a.multiply(b).subtract(c.multiply(d));
    System.out.println("A*B - C*D = "+answer+" = "+answer.toString(16));

    // Make one and test its integrity.
    SplitLong sla = new SplitLong(A);
    System.out.println("A="+Long.toHexString(A)+" ("+sla.toString()+") = "+Long.toHexString(sla.longValue()));

    // Start small.
    SplitLong sl10 = new SplitLong(10);
    SplitLong sl15 = new SplitLong(15);
    SplitLong sl150 = sl10.mul(sl15);
    System.out.println("10="+sl10.longValue()+"("+sl10.toString()+") * 15="+sl15.longValue()+"("+sl15.toString()+") = "+sl150.longValue() + " ("+sl150.toString()+")");

    // The real thing.
    SplitLong slb = new SplitLong(B);
    SplitLong slc = new SplitLong(C);
    SplitLong sld = new SplitLong(D);
    System.out.println("B="+Long.toHexString(B)+" ("+slb.toString()+") = "+Long.toHexString(slb.longValue()));
    System.out.println("C="+Long.toHexString(C)+" ("+slc.toString()+") = "+Long.toHexString(slc.longValue()));
    System.out.println("D="+Long.toHexString(D)+" ("+sld.toString()+") = "+Long.toHexString(sld.longValue()));
    SplitLong sanswer = sla.mul(slb).sub(slc.mul(sld));
    System.out.println("A*B - C*D = "+sanswer+" = "+sanswer.longValue());

  }

}

ছাপে:

A*B - C*D = 9223372036854775807 = 7fffffffffffffff
A=7fffffffffffffff (7fffffff|ffffffff) = 7fffffffffffffff
10=10(0|a) * 15=15(0|f) = 150 (0|96)
B=7ffffffffffffffe (7fffffff|fffffffe) = 7ffffffffffffffe
C=7fffffffffffffff (7fffffff|ffffffff) = 7fffffffffffffff
D=7ffffffffffffffd (7fffffff|fffffffd) = 7ffffffffffffffd
A*B - C*D = 7fffffff|ffffffff = 9223372036854775807

যা দেখে মনে হচ্ছে এটি কাজ করছে।

আমি বাজি ধরেছি যে আমি কিছু সূক্ষ্মতাগুলি হারিয়েছি যেমন সাইন ওভারফ্লো ইত্যাদির জন্য নজর রাখা ইত্যাদি তবে আমি মনে করি সারাংশ রয়েছে।


1
আমি মনে করি এটি @ ওফিরের পরামর্শের একটি বাস্তবায়ন।
ওল্ড কার্মিউডজিয়ন

2

সম্পূর্ণতার জন্য, যেহেতু কেউ এটির উল্লেখ করেনি, কিছু সংকলক (যেমন জিসিসি) আজকাল আপনাকে সত্যিকার অর্থে 128 বিট পূর্ণসংখ্যার সরবরাহ করে।

সুতরাং একটি সহজ সমাধান হতে পারে:

(long long)((__int128)A * B - (__int128)C * D)

1

AB-CD = (AB-CD) * AC / AC = (B/C-D/A)*A*C। নাও উপচে পড়তে B/Cপারে D/Aনা, তাই আগে গণনা করুন (B/C-D/A)। যেহেতু চূড়ান্ত ফলাফলটি আপনার সংজ্ঞা অনুসারে উপচে পড়বে না, আপনি নিরাপদে অবশিষ্ট গুণগুলি সম্পাদন করতে এবং গণনা করতে পারেন(B/C-D/A)*A*C প্রয়োজনীয় ফলাফলটি ।

মনে রাখবেন, আপনার ইনপুট হতে পারে অত্যন্ত ছোট পাশাপাশি, B/Cবা D/Aওভারফ্লো পারবেন না। যদি এটি সম্ভব হয় তবে ইনপুট পরিদর্শন অনুযায়ী আরও জটিল ম্যানিপুলেশনগুলির প্রয়োজন হতে পারে।


2
এটি কাজ করবে না কারণ পূর্ণসংখ্যা বিভাগ তথ্য হারিয়ে ফেলেছে (ফলাফলের ভগ্নাংশ)
অফির

@ ওফির এটি সঠিক, তবে আপনি কেক খেতে পারবেন না এবং এটি কোনও ছোঁয়া ছাড়তে পারবেন না। আপনাকে নির্ভুলতার মাধ্যমে বা অতিরিক্ত সংস্থান ব্যবহার করে (যেমন আপনি নিজের উত্তরের পরামর্শ দিয়েছিলেন) অর্থ প্রদান করতে হবে। আমার উত্তর গাণিতিক প্রকৃতির যখন আপনার 'কম্পিউটারমুখী'। প্রতিটি পরিস্থিতির উপর নির্ভর করে সঠিক হতে পারে।
সোমারউইটি ব্যবহারকারী নামটি

2
আপনি সঠিক - আমার অঙ্কটি এমনভাবে করা উচিত - অঙ্কটি সঠিক হওয়ায় কাজ করবে না বলে সঠিক ফলাফল দেবে না। যাইহোক, যে ক্ষেত্রে প্রশ্ন জমা দেওয়ার পক্ষে আগ্রহী (উদাহরণস্বরূপ প্রশ্নের উদাহরণে) উল্লেখ করুন ত্রুটিটি সম্ভবত আশ্চর্যজনকভাবে বড় হবে - কোনও ব্যবহারিক প্রয়োগের জন্য গ্রহণযোগ্য হতে পারে তার চেয়ে অনেক বড়। যাই হোক না কেন - এটি একটি অন্তর্দৃষ্টিপূর্ণ উত্তর ছিল এবং আমার সেই ভাষাটি ব্যবহার করা উচিত হয়নি।
অফির

@ ওফির আমি আপনার ভাষা অনুপযুক্ত মনে করি না। চূড়ান্ত সংস্থার সীমাবদ্ধতার অধীনে সম্পাদন করার জন্য নির্ভুলতা হারাবে না এমন নয়, এই ওপি স্পষ্টভাবে একটি "সঠিক" গণনার জন্য অনুরোধ করেছিল।
ব্যবহারকারী 481516234242

1

চয়ন করুন K = a big number(উদা। K = A - sqrt(A))

A*B - C*D = (A-K)*(B-K) - (C-K)*(D-K) + K*(A-C+B-D); // Avoid overflow.

কেন?

(A-K)*(B-K) = A*B - K*(A+B) + K^2
(C-K)*(D-K) = C*D - K*(C+D) + K^2

=>
(A-K)*(B-K) - (C-K)*(D-K) = A*B - K*(A+B) + K^2 - {C*D - K*(C+D) + K^2}
(A-K)*(B-K) - (C-K)*(D-K) = A*B - C*D - K*(A+B) + K*(C+D) + K^2 - K^2
(A-K)*(B-K) - (C-K)*(D-K) = A*B - C*D - K*(A+B-C-D)

=>
A*B - C*D = (A-K)*(B-K) - (C-K)*(D-K) + K*(A+B-C-D)

=>
A*B - C*D = (A-K)*(B-K) - (C-K)*(D-K) + K*(A-C+B-D)

নোট কারণ এ, বি, সি এবং ডি, বড় সংখ্যা এইভাবে A-Cএবং B-Dছোট নম্বর আছে।


আপনি কীভাবে ব্যবহারিকভাবে কে নির্বাচন করবেন ? এছাড়াও, কে * (এ-সি + বিডি) এখনও উপচে পড়তে পারে।
ylc

@ylc: কে = স্কয়ার্ট (এ) চয়ন করুন, A-C+B-Dএটি একটি ছোট সংখ্যা নয়। কারণ এ, বি, সি এবং ডি বড় সংখ্যা, সুতরাং এসি ছোট সংখ্যা।
আমির সানিয়ান

আপনি যদি কে = স্কয়ার্ট (এ) চয়ন করেন , তবে (একে) * (বিকে) আবার উপচে পড়তে পারে।
ylc

@ এলসি: ঠিক আছে! আমি এটি পরিবর্তনA - sqrt(A) :)
আমির সানিয়ান

তারপরে কে * (এ-সি + বিডি) উপচে পড়তে পারে।
ylc
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.