java - BigDecimal ضرب بصفر




numerical-analysis rounding-error (4)

أقوم بإجراء عملية الضرب بسيطة مع BigDecimal ولقد وجدت بعض السلوك الغريب عند ضرب من الصفر (ضرب من الصفر هو الصحيح في حالة الاستخدام).

تعلمني الرياضيات الأساسية أن أي شيء مضروب في الصفر سوف يساوي الصفر (انظر: خاصية المنتج الصفري وخصائص الضرب )

ومع ذلك ، ستفشل التعليمة البرمجية التالية باستمرار مع نفس الخطأ:

assertEquals(new BigDecimal(0), new BigDecimal(22.3).multiply(new BigDecimal(0)));
java.lang.AssertionError: 
Expected :0
Actual   :0E-48

هل هذا عدم دقة مع BigDecimal أم أن هناك فرعًا من الرياضيات يفقدني في مكان ما؟

ملاحظات: JDK 1.6.0_27 يعمل في IntelliJ 11


Answers

equals() على BigDecimal يتحقق من الحالة الداخلية BigDecimal للمقارنة

إحالة الرمز أدناه

public boolean equals(Object x) {
    if (!(x instanceof BigDecimal))
        return false;
    BigDecimal xDec = (BigDecimal) x;
    if (x == this)
        return true;
    if (scale != xDec.scale)
        return false;
    long s = this.intCompact;
    long xs = xDec.intCompact;
    if (s != INFLATED) {
        if (xs == INFLATED)
            xs = compactValFor(xDec.intVal);
        return xs == s;
    } else if (xs != INFLATED)
        return xs == compactValFor(this.intVal);

    return this.inflate().equals(xDec.inflate());
}

إذا كنت ترغب في مقارنة استخدام القيم compareTo()

تغيير التعليمات البرمجية الخاصة بك

assertEquals(0 , new BigDecimal(0).compareTo(new BigDecimal(22.3).multiply(new BigDecimal(0)));

تحديث:

استخدم منشئ مع الأخذ String كمعلمة لـ BigDecimal للتأكد من دقته في دقة التحقق من الروابط ذات الصلة أدناه

انظر أيضا


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

ومع ذلك ، يمكنك استخدام compareTo() للقيام بما تريد:

كما يشيرassylias ، يجب عليك أيضًا استخدام منشئ BigDecimal new BigDecimal("22.3") لتجنب مشاكل الدقة المزدوجة.

BigDecimal expected = BigDecimal.ZERO;
BigDecimal actual = new BigDecimal("22.3").multiply(BigDecimal.ZERO);
assertEquals(0, expected.compareTo(actual));

هناك أيضًا طريقة تُسمى signum() ، والتي تُرجع 1 أو 0 أو 1 للسلبية والصفر والإيجابية. لذا يمكنك أيضًا اختبار الصفر

assertEquals(0, actual.signum());

هناك مشكلتان مع شفرتك:

  • يجب عليك مقارنة BigDecimal مع compareTo بدلاً من الأندلس ، كما هو مذكور في الإجابات الأخرى
  • ولكن يجب أيضًا استخدام مُنشئ السلسلة: new BigDecimal("22.3") بدلاً من المُنشئ new BigDecimal(22.3) لتجنب مشكلات الدقة المزدوجة

بمعنى آخر ، التعليمة البرمجية التالية (التي تستخدم بشكل صحيح compareTo) مازال إرجاع false:

BigDecimal bd = new BigDecimal(0.1).multiply(new BigDecimal(10));
System.out.println(bd.compareTo(BigDecimal.ONE) == 0);

لأن 0.1d * 10d != 1


قد ترغب في استخدام equals () نظرًا لأنها كائنات ، واستخدام مثيل ZERO المضمّن:

if(selectPrice.equals(BigDecimal.ZERO))

لاحظ أن .equals() يأخذ القياس في الاعتبار ، لذلك ما لم يكن selectPrice هو نفس المقياس (0) كـ .ZERO فإن هذا سيؤدي إلى false.

لأخذ القياس من المعادلة كما هي:

if(selectPrice.compareTo(BigDecimal.ZERO) == 0)

ينبغي أن أشير إلى أنه في مواقف رياضية معينة ، 0.00 != 0 ، وهذا هو السبب في أنني أتصور .equals() يأخذ المقياس بعين الاعتبار. يعطي 0.00 الدقة إلى مكان المائة ، في حين أن 0 ليس دقيقًا. اعتمادا على الوضع قد ترغب في التمسك مع .equals() .





java bigdecimal numerical-analysis rounding-error