إستخدام double و float: كيف يؤدي إلى خسارة حتمية أو أرباح زائفة
استخدام BigDecimal للأسباب التي ذكرتها ليس حلا.
أي نظام رقمي سيعاني مشاكل من تمثيل بعض الكسور. النظام العشري لن يستطيع تمثيل 1/3 مثلا.
BigDecimal لن تفيد في ذلك. إذا كانت النسبة مهمة يجب تخزينها كنسبة (Rational).
ما ذكرته من مشاكل محتملة:
- ظهور الأرقام ككسور طويلة في الشاشة حين لم يتوقع المبرمج أن يحدث ذلك.
السبب عندك هو أن Double.toString() ترجع أدق تمثيل عشري لازم لمنع الاختلاط. الحل الصحيح هو التقريب للخانات العشرية المرادة عند العرض.
- في الأنظمة الحسابية يحدث إختلاف طفيف يكلف المُنشأة الكثير من الوقت و المال في محاولة إيجاد سبب الفرق.
صحيح. BigDecimal واجبة عند التعامل مع المبالغ المالية.
- في الأنظمة الطبية يؤدي إلى حصول المريض على جُرعة زائدة أو ناقصة.
الحلقة الضعيفة هي دقة الجهاز الذي يضخ الجرعة.
- في الأنظمة العسكرية يؤدي إلى سقوط صواريخ في غير ما خُطط له.
صحيح أن الاحداثيات المستخدمة عشرية. النوع المستخدم هنا عادة ما يكون FixedPoint.
- في أنظمة الفضاء يؤدي إلى إنحراف مكوك فضائي عن مساره!
لم تظن أن Decimal ستحل المشكلة؟ نفاثات المكوك لا تعبأ إن كنت تستخدم Decimal أو Binary Float أو حتى Fixed.
على أي حال دقة النوع المستخدم أعلى من دقة التحكم ويلزمك تصحيح المسار دائما.
نظم التحكم في الطيران الجارية بالفعل تستعمل binary floating point على عكس ما تلمح له.
النظام العشري لن تحتاجه إلا عندما تتعامل مع وحدات نحن صنعناها (فائدة تراكمية، خطوط طول وعرض).
ما عدا ذلك فكل النظم الرقمية سواسية.
شُكراً لك @a3f
ردُّك مليء بالمعلومات القيمة، جُزيت خيراً.
أعلم تماماً أن BigDecimal ليست الحل الوحيد، وليست الأسهل ولا الأفضل حتى. توقّعتُ أن هناك من سيُعلّق متحدثاً عن تلافي إستخدام الكسور و استبدالها بأرقام صحيحة أو استخدام Round أكثر ولكن ردّك أسعدني.
المشاكِل المذكورة هي ما قد يحدُث عند استخدام double أو float في هكذا أنظمة، وليس حلها دائماً وحيد، هناك حُلول كثيرة منها ما ذكرت.
أثريتَ النقاش أخي @a3f و قد اسعدني ردُّك مرة أخرى شكراً لك.
شكرًا على مجهودك، فقط لم توضح أنه لمبرمجي Java؛ عندما تحدثت عن الحل تحدثتَ BigDecimal مباشرًا،
على الجانب في Go وضعواْ نوعًا افتراضيًا complex128، وcomplex64 يحل المشكلة بسهولة وببساطة:
package main
import "fmt"
func main() {
num := complex128(6)
fmt.Printf("%f", num * 0.3)
}
التجربة:
فعلاً @يوسف سيد
القسم الذي كتبت به هذه التدوينة هو جافا، ولكن العنوان و الرابط لا يوضح هذه المعلومة.
سأجعل الأمور أوضح مستقبلاً بإذن الله.
لا أظن printf في Go تختلف عن أمها في C:
%f تقرب ل 6 خانات عشرية ما لم يذكر خلاف ذلك. ذلك على عكس الجافا التي ترجع أدق تمثيل عشري لازم لمنع الاختلاط.
حجم النوع ليس له أي علاقة. أرجح أن Complex تستخدم Binary Floating Point لشقيها كذلك.
حتى لو استخدمت BigBinary المشكلة مع 0.3 موجودة. وهي موجودة مع قيم أخرى في كل النظم. راجع تعليقي الآخر.
لا أظن printf في Go تختلف عن أمها في C:
%f تقرب ل 6 خانات عشرية ما لم يذكر خلاف ذلك. ذلك على عكس الجافا التي ترجع أدق تمثيل عشري لازم لمنع الاختلاط.
ليس تمامًا Go يمكنها معرفة النوع قبل الطباعة، وPrintf تختلف تمامًا عن C وC ليست أم Go هي أختها الكبيرة :)، انظر هنا:
والمصدر:
تستخدم Go معيار IEEE-754 في تمثيل الأرقام float32، float64 ..، قرأتُ الفرق أنّ Complex تحاول تقريب العدد إلى عدد حقيقي مع نسبة خطأ صغيرة جدًا على الكسور الصغير وللحقيقة لا أعرف كيف تمامًا، أستخدمها عند طباعة رقم حقيقي وإن كانت القسمة كبيرة أستخدم الحزمة math/big، في المثال السابق عند الطباعة بـPrintf سيكون الناتج:
1.800000+0.000000i
وعندما أستخدم الدالة real على الرقم النهائي لأحصل على أقرب float64 حقيقي، سأحصل على:
1.800000
من توثيق Go:
The default precision for %e and %f is 6
complex128 is the set of all complex numbers with float64 real and imaginary parts.
ما ذكرته ليس له علاقة بالموضوع. العدد معروض عندك كذلك لأن printf تقربه.
For floating-point values, width sets the minimum width of the field and precision sets the number of places after the decimal, if appropriate, except that for %g/%G it sets the total number of digits. For example, given 123.45 the format %6.2f prints 123.45 while %.4g prints 123.5. The default precision for %e and %f is 6; for %g it is the smallest number of digits necessary to identify the value uniquely.
ثم شاهد:
For complex numbers, the width and precision apply to the two components independently and the result is parenthesized, so %f applied to 1.2+3.4i produces (1.200000+3.400000i).
ما الأمر الذي لم تفهمه؟ Go تعرض النوع كذلك لأن Printf تقربه ليس لأنه مخزن بدقة. ما ذكرته أنت عن الدقة الزائدة لComplex و Float64 ليس له علاقة بعرض العدد ك 1.8.
آه حسنًا الآن فهمت، لكن لم أفهم بعد لما نستخدم cmplx.Pow بدلًا من math.Pow يقولون أنها أكثر دقة complex128 يعتمد على وحدتين!.
النوع Complex لتمثيل الأعداد المركبة. للأعداد الحقيقية (حيث الشطر التخيلي صفر) لا فرق بين إستخدام pow الخاصة به و pow الخاصة ب float عادي.
الأعداد المركبة منهج مقرر في الثانوية العامة المصرية لطلبة الرياضيات.
التعليقات