سؤال

هذا السؤال لديه بالفعل إجابة هنا:

يبدو أن الطرح يؤدي إلى حدوث مشكلة ما وأن القيمة الناتجة خاطئة.

double tempCommission = targetPremium.doubleValue()*rate.doubleValue()/100d;

78.75 = 787.5 * 10.0/100 د

double netToCompany = targetPremium.doubleValue() - tempCommission;

708.75 = 787.5 - 78.75

double dCommission = request.getPremium().doubleValue() - netToCompany;

877.8499999999999 = 1586.6 - 708.75

وستكون القيمة المتوقعة الناتجة 877.85.

ما الذي يجب فعله لضمان الحساب الصحيح؟

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

المحلول

للتحكم في دقة حساب النقطة العائمة، يجب عليك استخدام java.math.BigDecimal.يقرأ الحاجة إلى BigDecimal بواسطة جون زوكوفسكي لمزيد من المعلومات.

في ضوء المثال الخاص بك، سيكون السطر الأخير على النحو التالي باستخدام BigDecimal.

import java.math.BigDecimal;

BigDecimal premium = BigDecimal.valueOf("1586.6");
BigDecimal netToCompany = BigDecimal.valueOf("708.75");
BigDecimal commission = premium.subtract(netToCompany);
System.out.println(commission + " = " + premium + " - " + netToCompany);

وينتج عن هذا الإخراج التالي.

877.85 = 1586.6 - 708.75

نصائح أخرى

كما ذكرت الإجابات السابقة، هذا نتيجة للقيام بحساب النقطة العائمة.

كما اقترح ملصق سابق، عندما تقوم بالحسابات الرقمية، استخدم java.math.BigDecimal.

ومع ذلك، هناك مسكتك للاستخدام BigDecimal.عندما تقوم بالتحويل من القيمة المزدوجة إلى a BigDecimal, ، لديك خيار استخدام جديد BigDecimal(double) المنشئ أو BigDecimal.valueOf(double) طريقة المصنع الثابتة.استخدم طريقة المصنع الثابت.

يقوم المُنشئ المزدوج بتحويل الدقة الكاملة للملف double إلى أ BigDecimal بينما يقوم المصنع الثابت بتحويله بشكل فعال إلى ملف String, ، ثم يحول ذلك إلى a BigDecimal.

يصبح هذا ذا صلة عندما تواجه أخطاء التقريب الدقيقة هذه.قد يتم عرض الرقم كـ .585، ولكن قيمته داخليًا هي "0.58499999999999996447286321199499070644378662109375".إذا كنت تستخدم BigDecimal منشئ، سوف تحصل على الرقم الذي لا يساوي 0.585، في حين أن الطريقة الثابتة ستعطيك قيمة تساوي 0.585.

double value = 0.585;
System.out.println(new BigDecimal(value));
System.out.println(BigDecimal.valueOf(value));

على نظامي يعطي

0.58499999999999996447286321199499070644378662109375
0.585

مثال آخر:

double d = 0;
for (int i = 1; i <= 10; i++) {
    d += 0.1;
}
System.out.println(d);    // prints 0.9999999999999999 not 1.0

استخدم BigDecimal بدلاً من ذلك.

يحرر:

أيضًا، فقط للإشارة إلى أن هذه ليست مشكلة تقريب "Java".تضمن Java على الأقل سلوكًا ثابتًا في هذا الصدد.

سأقوم بتعديل المثال أعلاه كما يلي:

import java.math.BigDecimal;

BigDecimal premium = new BigDecimal("1586.6");
BigDecimal netToCompany = new BigDecimal("708.75");
BigDecimal commission = premium.subtract(netToCompany);
System.out.println(commission + " = " + premium + " - " + netToCompany);

بهذه الطريقة تتجنب مخاطر استخدام السلسلة في البداية.بديل آخر:

import java.math.BigDecimal;

BigDecimal premium = BigDecimal.valueOf(158660, 2);
BigDecimal netToCompany = BigDecimal.valueOf(70875, 2);
BigDecimal commission = premium.subtract(netToCompany);
System.out.println(commission + " = " + premium + " - " + netToCompany);

أعتقد أن هذه الخيارات أفضل من استخدام الزوجي.تبدأ الأرقام في تطبيقات الويب كسلاسل على أي حال.

في أي وقت تقوم فيه بإجراء حسابات زوجية، يمكن أن يحدث هذا.سيعطيك هذا الرمز 877.85:

إجابة مزدوجة = Math.round(dCommission * 100000) / 100000.0;

احفظ عدد السنتات بدلاً من الدولارات، وقم فقط بالتنسيق بالدولار عند إخراجه.بهذه الطريقة يمكنك استخدام عدد صحيح لا يعاني من مشكلات الدقة.

انظر الردود على هذا السؤال.ما تراه في الأساس هو نتيجة طبيعية لاستخدام حساب النقطة العائمة.

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

هذه مسألة ممتعة.

الفكرة وراء رد Timons هي تحديد epsilon الذي يمثل أصغر دقة يمكن أن تكون عليها المضاعفة القانونية.إذا كنت تعرف في طلبك أنك لن تحتاج أبدًا إلى دقة أقل من 0.00000001، فإن ما يقترحه يكفي للحصول على نتيجة أكثر دقة قريبة جدًا من الحقيقة.مفيد في التطبيقات التي يعرفون فيها الدقة القصوى مقدمًا (على سبيل المثال تمويل دقة العملة، وما إلى ذلك)

ومع ذلك، فإن المشكلة الأساسية في محاولة تقريبه هي أنه عند القسمة على عامل لإعادة قياسه، فإنك تقدم في الواقع احتمالًا آخر لمسائل الدقة.يمكن لأي تلاعب في الزوجي أن يؤدي إلى مشاكل عدم دقة بترددات متفاوتة.خاصة إذا كنت تحاول التقريب إلى رقم مهم جدًا (بحيث تكون معاملاتك <0) على سبيل المثال إذا قمت بتشغيل ما يلي باستخدام رمز Timons:

System.out.println(round((1515476.0) * 0.00001) / 0.00001);

سوف يؤدي إلى 1499999.9999999998 حيث الهدف هنا هو التقريب بوحدات 500000 (أي نريد 1500000)

في الواقع، الطريقة الوحيدة للتأكد تمامًا من التخلص من عدم الدقة هي المرور عبر BigDecimal للقياس.على سبيل المثال

System.out.println(BigDecimal.valueOf(1515476.0).setScale(-5, RoundingMode.HALF_UP).doubleValue());

إن استخدام مزيج من إستراتيجية epsilon واستراتيجية BigDecimal سيمنحك تحكمًا جيدًا في الدقة.فكرة كون epsilon تجعلك قريبًا جدًا ومن ثم سيزيل BigDecimal أي عدم دقة ناتج عن إعادة القياس بعد ذلك.على الرغم من أن استخدام BigDecimal سيؤدي إلى تقليل الأداء المتوقع لتطبيقك.

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

الطريقة الأكثر أناقة والأكثر فعالية حتى الآن للقيام بذلك في Java:

double newNum = Math.floor(num * 100 + 0.5) / 100;

الأفضل من ذلك أن تستخدم JScience نظرًا لأن BigDecimal محدود إلى حد ما (على سبيل المثال، لا توجد وظيفة sqrt)

double dCommission = 1586.6 - 708.75;
System.out.println(dCommission);
> 877.8499999999999

Real dCommissionR = Real.valueOf(1586.6 - 708.75);
System.out.println(dCommissionR);
> 877.850000000000
double rounded = Math.rint(toround * 100) / 100;

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

public static int round(Double i) {
    return (int) Math.round(i + ((i > 0.0) ? 0.00000001 : -0.00000001));
}

مثال:

    Double foo = 0.0;
    for (int i = 1; i <= 150; i++) {
        foo += 0.00010;
    }
    System.out.println(foo);
    System.out.println(Math.round(foo * 100.0) / 100.0);
    System.out.println(round(foo*100.0) / 100.0);

الذي يطبع:

0.014999999999999965
0.01
0.02

مزيد من المعلومات: http://en.wikipedia.org/wiki/Double_precision

انها بسيطة جدا.

استخدم عامل التشغيل %.2f للإخراج.تم حل المشكلة!

على سبيل المثال:

int a = 877.8499999999999;
System.out.printf("Formatted Output is: %.2f", a);

ينتج عن الكود أعلاه إخراج طباعة من:877.85

يحدد عامل التشغيل %.2f أنه يجب استخدام منزلتين عشريتين فقط.

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