كيفية تجنب التقريب عند مقارنة قيم العملة في دلفي؟

StackOverflow https://stackoverflow.com/questions/182475

سؤال

AFAIK ، يعتمد نوع العملة في Delphi Win32 على دقة النقطة العائمة المعالج. ولهذا السبب ، أواجه مشكلات تقريبية عند مقارنة قيمتين للعملة ، وإعادة نتائج مختلفة اعتمادًا على الجهاز.

في الوقت الحالي ، أستخدم وظيفة نفس القيمة التي تمرر معلمة epsilon = 0.009 ، لأنني بحاجة فقط إلى رقمين عشريين.

هل هناك طريقة أفضل لتجنب هذه المشكلة؟

هل كانت مفيدة؟

المحلول

نوع العملة في دلفي هو عدد صحيح 64 بت تم قياسه بمقدار 1/10،000 ؛ وبعبارة أخرى ، فإن أصغرها تعادل 0.0001. ليس من المعتاد المشكلات الدقيقة بنفس الطريقة التي يكون بها رمز النقطة العائمة.

ومع ذلك ، إذا كنت تضاعف أرقام العملات الخاصة بك عن طريق أنواع النقطة العائمة ، أو تقسيم قيم العملات الخاصة بك ، فيجب وضع التقريب بطريقة أو بأخرى. يتحكم FPU في هذه الآلية (تسمى "كلمة التحكم"). تحتوي وحدة الرياضيات على بعض الإجراءات التي تتحكم في هذه الآلية: SetRoundMode على وجه الخصوص. يمكنك رؤية التأثيرات في هذا البرنامج:

{$APPTYPE CONSOLE}

uses Math;

var
  x: Currency;
  y: Currency;
begin
  SetRoundMode(rmTruncate);
  x := 1;
  x := x / 6;
  SetRoundMode(rmNearest);
  y := 1;
  y := y / 6;
  Writeln(x = y); // false
  Writeln(x - y); // 0.0001; i.e. 0.1666 vs 0.1667
end.

من الممكن أن تكون مكتبة الطرف الثالث الذي تستخدمه تعيين كلمة التحكم بقيمة مختلفة. قد ترغب في تعيين كلمة التحكم (IE Rounding Mode) بشكل صريح في نقطة البداية لحساباتك المهمة.

أيضًا ، إذا نقلت حساباتك إلى نقطة عائمة عاديًا ثم تعود إلى العملة ، فإن جميع الرهانات متوقفة - من الصعب للغاية التدقيق. تأكد من أن جميع حساباتك بالعملة.

نصائح أخرى

لا ، العملة ليست نوعًا من النقطة العائمة. إنه عشري ثابت ، يتم تنفيذه مع تخزين عدد صحيح. يمكن مقارنتها بالضبط ، وليس لديها قضايا التقريب ، على سبيل المثال ، مزدوجة. لذلك ، إذا كنت ترى القيم غير الدقيقة في متغيرات العملات الخاصة بك ، فإن المشكلة ليست هي نوع العملة نفسها ، ولكن ما تضعه فيه. على الأرجح ، لديك حساب نقطة عائمة في مكان آخر في الكود الخاص بك. نظرًا لأنك لا تظهر هذا الرمز ، فمن الصعب أن تكون مزيد من المساعدة في هذا السؤال. ولكن الحل ، بشكل عام ، سيكون حول أرقام نقاط العائمة إلى الدقة الصحيحة قبل التخزين في متغير العملة ، بدلاً من إجراء مقارنة غير دقيقة على متغيرات العملة.

طريقة أسرع وأكثر أمانًا لمقارنة اثنين currency القيم هي بالتأكيد لرسم خريطة للمتغيرات إلى الداخلية Int64 التمثيل:

function CompCurrency(var A,B: currency): Int64;
var A64: Int64 absolute A;
    B64: Int64 absolute B;
begin
  result := A64-B64;
end;

سيتجنب ذلك أي خطأ في التقريب أثناء المقارنة (العمل مع *10000 قيم عدد صحيح) ، وسيكون أسرع من التنفيذ الافتراضي المستند إلى FPU (خاصة تحت 64 بتات XE2).

نرى هذه المقالة للحصول على معلومات إضافية.

إذا كان وضعك مثلني ، فقد تجد هذا النهج مفيدًا. أنا أعمل في الغالب في كشوف المرتبات. إذا قالت شركة ما من 3 أقسام وأراد فرض تكلفة الموظف بالتساوي بين تلك الإدارات الثلاثة ، فهناك عدة مرات عندما تكون هناك مشكلات حول التقريب.

ما كنت أفعله هو حلقة من خلال الإدارات التي تفرض كل واحد ثلث التكلفة الإجمالية وإضافة التكلفة المفروضة على متغير (عملة). ولكن عندما يساوي متغير الحلقة الحد ، بدلاً من الضرب بواسطة الكسر ، أطرح المتغير الفرعي من التكلفة الإجمالية وأضع ذلك في القسم الأخير. نظرًا لأن إدخالات المجلات التي تنتج عن هذه العملية يجب أن توازن دائمًا ، أعتقد أنها نجحت دائمًا.

انظر الموضوع:

D7 / Dunit: جميع اختبارات Checkequals (العملة ، العملة) تفشل فجأة ...

https://forums.codegear.com/thread.jspa؟threadid=16288

يبدو أن التغيير في محطات العمل التنمية لدينا تسبب في فشل مقارنة العملة. لم نجد السبب الجذري ، ولكن على جهاز كمبيوتر يعملان Windows 2000 SP4 ، ومستقل عن إصدار GDS32.dll (Interbase 7.5.1 أو 2007) و Delphi (7 و 2009) ، هذا الخط

TIBDataBase.Create(nil);

يغير قيمة كلمة التحكم 8087 من 1372 دولارًا إلى 1272 دولارًا الآن.

وسوف تفشل جميع مقارنات العملات في اختبارات الوحدة مع رسائل مضحكة مثل

Expected: <12.34> - Found: <12.34>

لم يتم تعديل GDS32.dll ، لذلك أعتقد أن هناك تبعية في هذه المكتبة على DLL من طرف ثالث تعدل كلمة التحكم.

لتجنب المشكلات المحتملة مع تقريب العملة في دلفي ، استخدم 4 أماكن عشرية.

سيضمن هذا عدم تواجد المشكلات التقريب أبدًا عند القيام بألعاب concualtions بكميات صغيرة جدًا.

"Been there. Done That. Written the unit tests."

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top