"جديد BigDecimal(13.3 د)" نتائج غير دقيقة "13.3000000000000007105.."?

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

  •  19-08-2019
  •  | 
  •  

سؤال

كيف جافا BigDecimal يمكن أن يكون هذا مؤلما ؟

Double d = 13.3D;

BigDecimal bd1 = new BigDecimal(d);
BigDecimal bd2 = new BigDecimal(String.valueOf(d));


System.out.println("RESULT 1: "+bd1.toString());
System.out.println("RESULT 2: "+bd2.toString());

RESULT 1: 13.300000000000000710542735760100185871124267578125
RESULT 2: 13.3

هل هناك أي حالة حيث النتيجة 1 المنشود ؟ أنا أعرف أن جافا 1.5 تغيرت toString() طريقة ولكن كان هذا المقصود العواقب ؟

كما أدرك ذلك BigDecimal وقد doubleValue() الخ, ولكن المكتبة التي أعمل مع مفيد يستخدم toString() ولا أستطيع تغيير ذلك :-(

الهتافات.

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

المحلول

حسنا ، API يعالج هذا التناقض الواضح في منشئ BigDecimal(double val):

  1. نتائج هذا البناء يمكن أن تكون غير متوقعة.قد نفترض أن الكتابة الجديدة BigDecimal(0.1) في جاوة يخلق BigDecimal التي تساوي بالضبط 0.1 (وهو بلا مقياس القيمة 1, مع مقياس من 1), ولكنه في الواقع متساوية إلى 0.1000000000000000055511151231257827021181583404541015625.لأن هذا هو 0.1 لا يمكن ممثلة بالضبط كما مزدوج (أو ، في هذا الشأن, كما كسر ثنائية أي المتناهي الطول).وبالتالي ، فإن قيمة التي يتم تمريرها في إلى منشئ هو لا يساوي بالضبط 0.1 المظاهر على الرغم من.

  2. سلسلة منشئ, من ناحية أخرى, هو تماما يمكن التنبؤ بها:كتابة جديدة BigDecimal("0.1") يخلق أ BigDecimal التي تساوي بالضبط 0.1, كما هو متوقع.لذلك ، فمن المستحسن عموما أن سلسلة منشئ أن تستخدم في الأفضلية هذا واحد.

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

المغزى من القصة:الألم يبدو ذاتيا فقط استخدام new BigDecimal(String val) أو BigDecimal.valueOf(double val) بدلا من ذلك =)

نصائح أخرى

ومشكلتك لها علاقة BigDecimal شيء، وكل شيء مع Double، والتي لا يمكن أن تمثل 13.3 بدقة، لأنه يستخدم كسور الثنائية داخليا.

وهكذا هو عرض الخطأ الخاصة بك في السطر الأول جدا. وBigDecimal أولا ببساطة يحافظ عليه، في حين String.valueOf() يفعل بعض التقريب مريب الذي يتسبب في ثانية واحدة أن يكون المحتوى المطلوب، الى حد كبير من خلال الحظ.

قد ترغب في إبلاغ نفسك حول كيفية القيم الفاصلة العائمة تنفيذ (IEEE 754-1985).و فجأة كل شيء سوف تصبح واضحة تماما.

وهذا ليس خطأ من BigDecimal - انها خطأ من double. BigDecimal يمثل بدقة <م> بالضبط قيمة d. String.valueOf تظهر إلا نتيجة لعدد قليل من الأماكن العشرية.

الكسور تمثل الثنائية مع عدد أنواع(أي double, float) لا يمكن بدقة المخزنة في تلك الأنواع.

    Double d = 13.3;        
    BigDecimal bdNotOk = new BigDecimal(d);
    System.out.println("not ok: " + bdNotOk.toString());

    BigDecimal bdNotOk2 = new BigDecimal(13.3);
    System.out.println("not ok2: " + bdNotOk2.toString());

    double x = 13.3;
    BigDecimal ok = BigDecimal.valueOf(x);
    System.out.println("ok: " + ok.toString());

    double y = 13.3;
    // pretty lame, constructor's behavior is different from valueOf static method
    BigDecimal bdNotOk3 = new BigDecimal(y);
    System.out.println("not ok3: " + bdNotOk3.toString());

    BigDecimal ok2 = new BigDecimal("13.3");
    System.out.println("ok2: " + ok2.toString());

    Double e = 0.0;
    for(int i = 0; i < 10; ++i) e = e + 0.1; // some fractions cannot be accurately represented with binary
    System.out.println("not ok4: " + e.toString()); // should be 1


    BigDecimal notOk5 = BigDecimal.valueOf(e);
    System.out.println("not ok5: " + notOk5.toString()); // should be 1

    /* 
     * here are some fractions that can be represented exactly in binary:
     * 0.5   = 0.1   = 1 / 2
     * 0.25  = 0.01  = 1 / 4
     * 0.75  = 0.11  = 3 / 4
     * 0.125 = 0.001 = 1 / 8
     */

الإخراج:

not ok: 13.300000000000000710542735760100185871124267578125
not ok2: 13.300000000000000710542735760100185871124267578125
ok: 13.3
not ok3: 13.300000000000000710542735760100185871124267578125
ok2: 13.3
not ok4: 0.9999999999999999
not ok5: 0.9999999999999999

مجرد استخدام BigDecimal.valueOf(d) أو new BigDecimal(s).

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