سؤال

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

هذا البيان:

1876.8 == BigDecimal('1876.8')

إرجاع خطأ.

إذا قمت بتشغيل هذه القيم من خلال سلسلة التنسيق "%.13f" انا حصلت:

"%.20f" % 1876.8 => 1876.8000000000000
"%.20f" % BigDecimal('1876.8') => 1876.8000000000002

لاحظ إضافي 2 من التصويري في المكان العشري الأخير.

اعتقدت أن الكبير كان من المفترض أن تواجه عدم الدقة في تخزين الأرقام الحقيقية مباشرة في النقطة العائمة الأصلية للكمبيوتر. أين هذا 2 قادم من؟

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

المحلول

أنت على حق، يجب تخزين BigdeCimal بشكل صحيح، أفضل تخمين هو:

  • bigdecimal يخزن القيمة بشكل صحيح
  • عند المرور إلى وظيفة تنسيق سلسلة، يتم إلقاء التصوير الكبير كقيمة نقطة عائمة دقة أقل، مما يخلق ... 02.
  • عند مقارنتها مباشرة مع تعويم، فإن التعويم يحتوي على مكان عشري إضافي أبعد من 20 ترى (لا يمكن مقارنة العوامات الكلاسيكية بالسلوك).

في كلتا الحالتين، من غير المرجح أن تحصل على نتائج دقيقة تقارن تعويما بتكرارا.

نصائح أخرى

لن يمنحك التحكم في عدد الأماكن العشرية، ولكن آلية التنسيق التقليدي يبدو أن تكون:

a.to_s('F')

إذا كنت بحاجة إلى مزيد من التحكم، ففكر في استخدام GEM Money Gem، على افتراض أن مشكلة المجال هي في الغالب حول العملة.

gem install money

لا تقارن FPU الكسور الخاطئة العشرية للمساواة

المشكلة هي أن مقارنة المساواة ذات القيمة العائمة أو المزدوجة مع ثابت عشري يحتوي على جزء نادرا ما يكون ناجحا.

عدد قليل جدا من الكسور الخيطية عشرية لها قيم دقيقة في تمثيل FP الثنائي، لذلك عادة ما تكون مقارنات المساواة مصيرها عادة.*

للإجابة على سؤالك بالضبط، 2 يأتي من تحويل مختلف قليلا من كسر السلسلة العشرية في Float صيغة. نظرا لأن الكسر لا يمكن تمثيله بالضبط، فمن المحتمل أن تنظر حسابان كميات مختلفة من الدقة في الحسابات الوسيطة وتنتهي في نهاية المطاف تقريب الناتج عن IEEE 754 مزدوج 52 بت Double Mantissa بشكل مختلف. لا يكاد يهم لأنه هناك هو لا يوجد تمثيل دقيق على أي حال، ولكن واحد ربما هو أكثر خطأ من الآخر.

على وجه الخصوص، الخاص بك 1876.8 لا يمكن تمثيلها بالضبط من قبل كائن FP، في الواقع، بين 0.01 و 0.99، 0.25 فقط، 0.50، و 0.75 لها تمثيل ثنائي دقيق. جميع الآخرين، وتشمل 1876.8، كرر إلى الأبد وتقريبها إلى 52 بت. هذا هو حوالي نصف السبب في وجود bigdecimal حتى. (النصف الآخر من السبب هو الدقة الثابتة لبيانات FP: في بعض الأحيان تحتاج إلى المزيد.)

لذلك، فإن النتيجة التي تحصل عليها عند مقارنة قيمة الآلة الفعلية مع ثابت سلسلة عشرية يعتمد على كل بت واحد في الكسر الثنائي ... وصولا إلى 1/252 ... وحتى عندها يتطلب التقريب.

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

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

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


*المشكلة: أرقام الجهاز هي X / 2ن, ، ولكن الثوابت العشرية هي X / (2ن * 5م). القيمة الخاصة بك كعلامة، المتأثر، والمانيسا هي التكرار بلا حدود 0 10000001001 1101010100110011001100110011001100110011001100110011... من الناحية النافصة، فإن الحساب FP هو مقارنات دقيقة ودقيقة للمساواة تعمل بشكل جيد تماما عندما تكون القيمة ليس لها جزء.

كما قال ديفيد، فإن التصوير الدقيق يخزنه بشكل صحيح

 p (BigDecimal('1876.8') * 100000000000000).to_i

إرجاع 187680000000000000.

لذلك، نعم، تنسيق السلسلة هو تدميرها

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

على ماك OS X، أنا أركض ruby 1.8.7 (2008-08-11 patchlevel 72) [i686-darwin9]

irb(main):004:0> 1876.8 == BigDecimal('1876.8') => true

ومع ذلك، فإنني روبي، أعتقد أنك يجب أن تفكر في شروط الرسائل المرسلة إلى الكائنات. ماذا يعود هذا لك:

BigDecimal('1876.8') == 1876.8

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

لنفس السبب، لا أعتقد أن تنسيق التنسيق الدقيق عن طريق إرسال رسالة تنسيق إلى سلسلة التنسيق هو النهج الصحيح أيضا.

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