ما هو الأول المزدوج الذي ينحرف من دلتا الطويل المقابل؟

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

  •  06-09-2019
  •  | 
  •  

سؤال

أريد أن أعرف أول مضاعفة من 0D للأعلى التي تنحرف منذ فترة طويلة من "نفس القيمة" من قبل بعض دلتا، قل 1E-8. أنا فشل هنا رغم ذلك. أحاول القيام بذلك في ج رغم أنني عادة ما أستخدم اللغات المدارة، فقط في حالة. الرجاء المساعدة.


#include <stdio.h>
#include <limits.h>
#define DELTA 1e-8

int main() {
    double d = 0; // checked, the literal is fine
    long i;
    for (i = 0L; i < LONG_MAX; i++) {
         d=i; // gcc does the cast right, i checked
         if (d-i > DELTA || d-i < -DELTA) {
              printf("%f", d);
              break;
         }
    }
}

أنا أظن أن المشكلة هي أن di يلقي أن أضعفت، وبالتالي د == أنا ثم الفرق هو دائما 0. كيف يمكنني اكتشاف هذا بشكل صحيح - سأفضل المرح C صب القارنات خذ إلى الأبد.

إجابه: هو بالضبط كما توقعنا. 2 ^ 53 + 1 = 9007199254740993 هو النقطة الأولى من الفرق وفقا لأدوات C / UNIX / POSIX القياسية. شكرا جزيلا على باكس لبرنامجه. وأعتقد أن الرياضيات تفوز مرة أخرى.

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

المحلول

الزوجي في IEE754 لها دقة من 52 بت مما يعني أنهم يمكنهم تخزين الأرقام بدقة حتى (على الأقل) 251.

إذا كانت سيرا على الأوقات سعيدة 32 بت، فستكون لديهم فقط المدى (الإيجابي) 0 إلى 231 لذلك لا يوجد طول 32 بت ممثلا بالضبط كمضاعف. لفترة طويلة 64 بت، سيكون (تقريبا) 252 لذلك سأبدأ من هناك، وليس في الصفر.

يمكنك استخدام البرنامج التالي للكشف عن المكان الذي تبدأ في حدوث الفشل. نسخة سابقة كنت قد اعتمدت على حقيقة أن الرقم الأخير في عدد يتضاعف باستمرار يتبع التسلسل {2،4،8،6}. ومع ذلك، اخترت في نهاية المطاف لاستخدام أداة موثوقة معروفة (bc) للتحقق من الرقم كله، ليس فقط الرقم الأخير.

ضع في اعتبارك أن هذا مايو تتأثر بعملية sprintf() بدلا من دقة الزوجي الحقيقية (لا أعتقد أنه شخصيا لأنه ليس لديه أي مشاكل مع بعض الأرقام حتى 2143).

هذا هو البرنامج:

#include <stdio.h>
#include <string.h>

int main() {
    FILE *fin;
    double d = 1.0; // 2^n-1 to avoid exact powers of 2.
    int i = 1;
    char ds[1000];
    char tst[1000];

    // Loop forever, rely on break to finish.
    while (1) {
        // Get C version of the double.
        sprintf (ds, "%.0f", d);

        // Get bc version of the double.
        sprintf (tst, "echo '2^%d - 1' | bc >tmpfile", i);
        system(tst);
        fin = fopen ("tmpfile", "r");
        fgets (tst, sizeof (tst), fin);
        fclose (fin);
        tst[strlen (tst) - 1] = '\0';

        // Check them.
        if (strcmp (ds, tst) != 0) {
            printf( "2^%d - 1 <-- bc failure\n", i);
            printf( "   got       [%s]\n", ds);
            printf( "   expected  [%s]\n", tst);
            break;
        }

        // Output for status then move to next.
        printf( "2^%d - 1 = %s\n", i, ds);
        d = (d + 1) * 2 - 1;  // Again, 2^n - 1.
        i++;
    }
}

هذا يستمر حتى:

2^51 - 1 = 2251799813685247
2^52 - 1 = 4503599627370495
2^53 - 1 = 9007199254740991
2^54 - 1 <-- bc failure
   got       [18014398509481984]
   expected  [18014398509481983]

وهو ما يدور حول المكان الذي أتوقعه فشله.

كقدم، أنا استخدمت أعداد في الأصل من النموذج 2ن ولكن هذا حصلني على:

2^136 = 87112285931760246646623899502532662132736
2^137 = 174224571863520493293247799005065324265472
2^138 = 348449143727040986586495598010130648530944
2^139 = 696898287454081973172991196020261297061888
2^140 = 1393796574908163946345982392040522594123776
2^141 = 2787593149816327892691964784081045188247552
2^142 = 5575186299632655785383929568162090376495104
2^143 <-- bc failure
   got       [11150372599265311570767859136324180752990210]
   expected  [11150372599265311570767859136324180752990208]

مع حجم كونه مزدوج 8 بايت (فحص مع sizeof). اتضح أن هذه الأرقام كانت من النموذج الثنائي "1000..." والتي يمكن تمثيلها لفترة أطول بكثير مع الزوجي. هذا عندما تحولت إلى استخدام 2ن-1 للحصول على نمط بت أفضل: جميع بت واحد.

نصائح أخرى

في الطابق الأول "من الخطأ" عند الإدلاء على مزدوج لن يتم إيقاف تشغيله من قبل 1E-8، فسوف يتم إيقاف تشغيله بواسطة 1. طالما أن المزدوج يمكن أن يناسب طويل في أهميته، وسوف يمثله بدقة.

نسيت بالضبط عدد البتات المزدوجة على إزاحة الدقة مقابل الإزاحة، ولكن هذا من شأنه أن يخبرك بالحجم الأقصى الذي يمكن أن يمثله. يجب أن يكون لدى الطابق الأول من الخطأ أن يكون له النموذج الثنائي 10000 ... حتى تتمكن من العثور عليه بشكل أسرع بكثير من خلال البدء في 1 والأيسر المتغيرة.

تقول ويكيبيديا إن 52 بت في هذا الحد الأدنى، وليس عد البداية الضمنية 1. التي يجب أن تعني أن تكون الطويلة الأولى لصالحها إلى قيمة مختلفة هي 2 ^ 53.

على الرغم من أنني أذكر أن أذكر Fortran 95 وخلافيات في هذه المناقشة، سأذكر أن Fortran منذ معيار عام 1990 عرضت وظيفة جوهرية تباعد تخبرك ما الفرق بين الفرق بين الأنواع القابلة للتمثيل حول حقيقي معين. يمكنك القيام ببحث ثنائي على هذا، والتوقف عند التباعد (X)> دلتا. بالنسبة للمجمعات المجمعين التي تستخدم نفس نموذج النقطة العائمة كتلك التي تهتم بها (من المرجح أن تكون معيار IEEE754)، يجب أن تحصل على نفس النتائج.

قبالة اليد، اعتقدت أن الزوجي يمكن أن يمثل جميع الأعداد الصحيحة (داخل حدودهم) بالضبط.

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

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