ম্যাথ.উইন্ড (0.49999999999999994) 1 কেন ফিরে আসে?


567

নিম্নলিখিত প্রোগ্রামে আপনি দেখতে পাচ্ছেন যে প্রতিটি মান .5বাদে গোল করে দেওয়া থেকে কিছুটা কম 0.5

for (int i = 10; i >= 0; i--) {
    long l = Double.doubleToLongBits(i + 0.5);
    double x;
    do {
        x = Double.longBitsToDouble(l);
        System.out.println(x + " rounded is " + Math.round(x));
        l--;
    } while (Math.round(x) > i);
}

কপি করে প্রিন্ট

10.5 rounded is 11
10.499999999999998 rounded is 10
9.5 rounded is 10
9.499999999999998 rounded is 9
8.5 rounded is 9
8.499999999999998 rounded is 8
7.5 rounded is 8
7.499999999999999 rounded is 7
6.5 rounded is 7
6.499999999999999 rounded is 6
5.5 rounded is 6
5.499999999999999 rounded is 5
4.5 rounded is 5
4.499999999999999 rounded is 4
3.5 rounded is 4
3.4999999999999996 rounded is 3
2.5 rounded is 3
2.4999999999999996 rounded is 2
1.5 rounded is 2
1.4999999999999998 rounded is 1
0.5 rounded is 1
0.49999999999999994 rounded is 1
0.4999999999999999 rounded is 0

আমি জাভা 6 আপডেট 31 ব্যবহার করছি।


1
জাভা 1.7.0 এ এটি ঠিক কাজ করে i.imgur.com/hZeqx.png
কফি

2
@ অ্যাডেল: অলির উত্তর সম্পর্কে আমার মন্তব্য দেখুন , দেখে মনে হচ্ছে জাভা 6 এটির (এবং এটির সাথে দস্তাবেজগুলি ) এমনভাবে প্রয়োগ 0.5করে যা সংখ্যায় যোগ করে এবং তারপরে ব্যবহার করে যথাযথতার আরও ক্ষতি হতে পারে floor; জাভা 7 আর সেভাবে এটি নথিভুক্ত করে না (সম্ভবত / আশা করা যায় যে তারা এটি স্থির করেছেন)।
টিজে ক্রাউডার

1
এটি আমি লিখেছি একটি পরীক্ষা প্রোগ্রামে একটি বাগ ছিল। ;)
পিটার লরে

1
অন্য একটি উদাহরণ যা ভাসমান পয়েন্টের মানগুলি দেখায় তা মুখের মান হিসাবে নেওয়া যায় না।
মিছল রায়

1
এটি সম্পর্কে চিন্তা করার পরে। আমি কোন সমস্যা দেখছি না 0.4999999999999999994 সর্বনিম্ন প্রতিনিধিত্বযোগ্য সংখ্যার তুলনায় 0.5 এর চেয়ে কম এবং দশমিক মানব-পঠনযোগ্য আকারে উপস্থাপনা নিজেই একটি আনুমানিক যা আমাদের বোকা বানানোর চেষ্টা করছে।
মিশেল রায়

উত্তর:


574

সারসংক্ষেপ

জাভা In (এবং সম্ভবত পূর্বে) round(x)তে প্রয়োগ করা হয়েছে floor(x+0.5)1 এটি একটি স্পেসিফিকেশন বাগ, স্পষ্টভাবে এটির জন্য একটি প্যাথলজিকাল কেস। 2 জাভা 7 আর এই ভাঙা বাস্তবায়নের আদেশ দেয় না। 3

সমস্যাটি

0.5 + 0.49999999999999994 ডাবল নির্ভুলতায় ঠিক 1:

static void print(double d) {
    System.out.printf("%016x\n", Double.doubleToLongBits(d));
}

public static void main(String args[]) {
    double a = 0.5;
    double b = 0.49999999999999994;

    print(a);      // 3fe0000000000000
    print(b);      // 3fdfffffffffffff
    print(a+b);    // 3ff0000000000000
    print(1.0);    // 3ff0000000000000
}

এর কারণ এটি হল 0.4999999999999999994 এর 0.5 এর চেয়ে কম ক্ষুদ্রাকৃতি থাকে, সুতরাং যখন তারা যুক্ত হয় তখন এর ম্যান্টিসা স্থানান্তরিত হয় এবং ইউএলপি আরও বড় হয়।

সমাধান

জাভা Since সাল থেকে, ওপেনজেডিকে (উদাহরণস্বরূপ) এটিকে এভাবে প্রয়োগ করে: 4

public static long round(double a) {
    if (a != 0x1.fffffffffffffp-2) // greatest double value less than 0.5
        return (long)floor(a + 0.5d);
    else
        return 0;
}

1. http://docs.oracle.com/javase/6/docs/api/java/lang/Math.html#round%28double%29

2. http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6430675 (এটি সন্ধানের জন্য @ সিমননিকারসনকে ক্রেডিট)

৩. http://docs.oracle.com/javase/7/docs/api/java/lang/Math.html#round%28double%29

4. http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/7u40-b43/java/lang/Math.java#Math.round%28double%29


আমি যে সংজ্ঞা দেখতে না পান roundজন্য JavadocMath.round অথবা ওভারভিউতে Mathবর্গ।
টিজে ক্রোডার

3
@ অলি: ওহ এখন এটি আকর্ষণীয়, তারা জাভা 7 (যে ডক্সগুলির সাথে আমি লিঙ্ক করেছি) -এর জন্য কিছুটা বেরিয়ে এসেছি - সম্ভবত নির্ভুলতার (আরও) ক্ষতি হ্রাস করার মাধ্যমে এই ধরণের অদ্ভুত আচরণের কারণ এড়াতে হবে।
টিজে ক্রোডার

@ টিজে ক্রাউডার: হ্যাঁ, এটি আকর্ষণীয়। আপনি কি জানেন যে পৃথক জাভা সংস্করণগুলির জন্য কোনও ধরণের "রিলিজ নোট" / "উন্নতি" ডক আছে, যাতে আমরা এই অনুমানটি যাচাই করতে পারি?
অলিভার চার্লসওয়ার্থ


1
আমি সাহায্য করতে পারি না তবে ভাবতে পারি যে এই ফিক্সটি কেবল প্রসাধনী, কারণ শূন্যটি সর্বাধিক দৃশ্যমান। এই বৃত্তাকার ত্রুটি দ্বারা প্রভাবিত অন্যান্য অনেক ভাসমান পয়েন্টের মান সন্দেহ নেই।
মিশাল রায়

232

এটি একটি পরিচিত বাগ হিসাবে দেখা যাচ্ছে ( জাভা বাগ 6430675: ম্যাথ.াউন্ডে 0x1.fffffffffffffp-2 এর জন্য বিস্ময়কর আচরণ রয়েছে) যা জাভা 7-এ স্থির করা হয়েছে।


5
+1: ভাল লাগবে! আমার উত্তরে বর্ণিত জাভা 6 এবং 7 এর মধ্যে ডকুমেন্টেশনের পার্থক্যের সাথে সম্পর্ক রয়েছে।
অলিভার চার্লসওয়ার্থ


83

জেডিকে Source-তে উত্স কোড:

public static long round(double a) {
    return (long)Math.floor(a + 0.5d);
}

জেডিকে Source-তে উত্স কোড:

public static long round(double a) {
    if (a != 0x1.fffffffffffffp-2) {
        // a is not the greatest double value less than 0.5
        return (long)Math.floor(a + 0.5d);
    } else {
        return 0;
    }
}

JDK 6 এ যখন মান 0.4999999999999999994 ডি হয়, তখন এটি মেঝেতে কল করবে এবং তাই 1 টি প্রত্যাবর্তন করবে , কিন্তু জেডিকে 7 তে ifশর্তটি যাচাই করছে যে সংখ্যাটি সর্ব্বোচ্চ ডাবল মান 0.5 বা তার চেয়ে কম নয় কিনা checking এই ক্ষেত্রে হিসাবে সংখ্যাটি 0.5 এর চেয়ে কম ডাবল মান নয়, সুতরাং elseব্লকটি 0 প্রদান করে।

আপনি 0.4999999999999999999d চেষ্টা করতে পারেন, যা 1টি ফিরে আসবে, তবে 0 নয়, কারণ এটি সর্বকালের বৃহত্তম ডাবল মান 0.5 এর চেয়ে কম।


তারপর এখানে 1.499999999999999994 সঙ্গে কি ঘটে? রিটার্ন 2? এটি 1 ফিরে আসবে, তবে এটি আপনাকে আগের মতো একই ত্রুটি পাওয়া উচিত তবে 1 দিয়ে?
মিমি

6
1.49999999999999999994 ডাবল-স্পষ্টতা ভাসমান-পয়েন্টে প্রতিনিধিত্ব করা যায় না। 1.49999999999999988 1.5 এর চেয়ে কম ডাবল। আপনি প্রশ্নটি থেকে দেখতে পারেন, floorপদ্ধতিটি এটি সঠিকভাবে গোল করে।
অরেঞ্জডোগ

26

আমি JDK 1.6 32-বিটে একই পেয়েছি, তবে জাভা 7 64৪-বিট-এ আমি 0.49999999999999994 এর জন্য 0 পেয়েছি যা 0 হয় এবং শেষ লাইনটি মুদ্রিত হয় না। এটি একটি ভিএম সমস্যা বলে মনে হচ্ছে, তবে, ভাসমান পয়েন্টগুলি ব্যবহার করে, আপনাকে বিভিন্ন পরিবেশে (সিপিইউ, 32- বা 64-বিট মোড) ফলাফলগুলি কিছুটা আলাদা হওয়ার আশা করা উচিত।

এবং, ব্যবহার করার সময় round ম্যাট্রিক্স ইত্যাদি বা উল্টানোর সময় এই বিটগুলি একটি বিশাল পার্থক্য আনতে পারে।

x64 আউটপুট:

10.5 rounded is 11
10.499999999999998 rounded is 10
9.5 rounded is 10
9.499999999999998 rounded is 9
8.5 rounded is 9
8.499999999999998 rounded is 8
7.5 rounded is 8
7.499999999999999 rounded is 7
6.5 rounded is 7
6.499999999999999 rounded is 6
5.5 rounded is 6
5.499999999999999 rounded is 5
4.5 rounded is 5
4.499999999999999 rounded is 4
3.5 rounded is 4
3.4999999999999996 rounded is 3
2.5 rounded is 3
2.4999999999999996 rounded is 2
1.5 rounded is 2
1.4999999999999998 rounded is 1
0.5 rounded is 1
0.49999999999999994 rounded is 0

জাভা 7-এ (এটি পরীক্ষা করার জন্য আপনি যে সংস্করণটি ব্যবহার করছেন) বাগটি ঠিক করা হয়েছে।
ইভান পেরেজ

1
আমি মনে করি আপনি 32 বিট বোঝাতে চেয়েছিলেন আমি সন্দেহ করি যে এন.ইউইকিপিডিয়া.আর / উইকি / জেডআবিআরএ_০২২ কম্পিউটার ৯৯৯ জাভা চালাতে পারে এবং আমার সন্দেহ যে এরপরে একটি ৩৩ বিট মেশিন রয়েছে।
chx

@ সিএইচএক্স বেশ স্পষ্টতই, কারণ আমি এর আগে 32 বিট লিখেছি :)
ডানুবিয়ান নাবিক

11

উত্তর এর পরে একটি ওরাকল বাগ রিপোর্ট 6430675 এর একটি অংশ এ অংশ। সম্পূর্ণ ব্যাখ্যার জন্য রিপোর্টটি দেখুন।

পদ্ধতিগুলি - ম্যাথ, স্ট্রাইকমাথ.উইন্ডগুলি অপারেশনালি হিসাবে সংজ্ঞায়িত করা হয়

(long)Math.floor(a + 0.5d)

দ্বৈত যুক্তি জন্য। এই সংজ্ঞাটি সাধারণত প্রত্যাশার মতো কাজ করলেও 0x1.ffffffffffffffp-2 (0.49999999999999994) এর ক্ষেত্রে 0 এর পরিবর্তে 1 এর বিস্ময়কর ফলাফল দেয়।

0.4999999999999999994 মানটি 0.5 এর চেয়ে কম বৃহত্তম ভাসমান-পয়েন্টের মান। হেক্সাডেসিমাল ভাসমান-পয়েন্ট আক্ষরিক হিসাবে এর মান 0x1.fffffffffffffffp-2, যা সমান (2 - 2 ^ 52) * 2 ^ -2। == (0.5 - 2 ^ 54)। সুতরাং, যোগফলের সঠিক মান

(0.5 - 2^54) + 0.5

1 - 2 ^ 54 হয়। এটি দুটি সংলগ্ন ভাসমান-পয়েন্ট সংখ্যা (1 - 2 ^ 53) এবং 1 এর মাঝামাঝি Java উপস্থাপনযোগ্য ভাসমান-পয়েন্টের মানগুলি যা বন্ধনী দেয় সঠিক ফলাফলটি অবশ্যই ফিরে আসতে হবে; যদি উভয় মান সমানভাবে কাছাকাছি হয় তবে তার সর্বশেষ বিট শূন্যটি ফিরে আসে। এক্ষেত্রে অ্যাড থেকে সঠিক রিটার্ন মান হ'ল 1, সবচেয়ে বড় মান 1 এর চেয়ে কম নয়।

পদ্ধতিটি নির্ধারিত হিসাবে পরিচালনা করার সময়, এই ইনপুটটিতে আচরণটি খুব অবাক করে; স্পেসিফিকেশনটির আরও কিছু সংশোধন করা যেতে পারে যেমন "নিকটতম দীর্ঘ, রাউন্ডিং টেন্ডস আপ", যা এই ইনপুটটির আচরণ পরিবর্তন করতে দেয়।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.