لماذا تحتوي "dtoa.c" على الكثير من التعليمات البرمجية؟

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

سؤال

سأكون أول من يعترف بأن معرفتي العامة بالبرمجة ذات المستوى المنخفض متناثر بعض الشيء. أفهم العديد من المفاهيم الأساسية لكنني لا أستخدمها بشكل منتظم. بعد أن قيل أنني كنت مندهشًا تمامًا من مقدار الكود اللازم له dtoa.c.

على مدار الشهرين الماضيين ، كنت أعمل على تطبيق ECMAScript في C# وكنت أتباطأ ملء الثقوب في محركي. الليلة الماضية بدأت العمل عليها number.prototype.toString الذي تم وصفه في القسم 15.7.4.2 التابع مواصفات ecmascript (بي دي إف). في قسم 9.8.1, ، يوفر الملاحظة 3 رابطًا إلى dtoa.c لكنني كنت أبحث عن تحد ، لذا انتظرت مشاهدته. ما يلي ما توصلت إليه.

private IDynamic ToString(Engine engine, Args args)
{
    var thisBinding = engine.Context.ThisBinding;
    if (!(thisBinding is NumberObject) && !(thisBinding is NumberPrimitive))
    {
        throw RuntimeError.TypeError("The current 'this' must be a number or a number object.");
    }

    var num = thisBinding.ToNumberPrimitive();

    if (double.IsNaN(num))
    {
        return new StringPrimitive("NaN");
    }
    else if (double.IsPositiveInfinity(num))
    {
        return new StringPrimitive("Infinity");
    }
    else if (double.IsNegativeInfinity(num))
    {
        return new StringPrimitive("-Infinity");
    }

    var radix = !args[0].IsUndefined ? args[0].ToNumberPrimitive().Value : 10D;

    if (radix < 2D || radix > 36D)
    {
        throw RuntimeError.RangeError("The parameter [radix] must be between 2 and 36.");
    }
    else if (radix == 10D)
    {
        return num.ToStringPrimitive();
    }

    var sb = new StringBuilder();
    var isNegative = false;

    if (num < 0D)
    {
        isNegative = true;
        num = -num;
    }

    var integralPart = Math.Truncate(num);
    var decimalPart = (double)((decimal)num.Value - (decimal)integralPart);
    var radixChars = RadixMap.GetArray((int)radix);

    if (integralPart == 0D)
    {
        sb.Append('0');
    }
    else
    {
        var integralTemp = integralPart;
        while (integralTemp > 0)
        {
            sb.Append(radixChars[(int)(integralTemp % radix)]);
            integralTemp = Math.Truncate(integralTemp / radix);
        }
    }

    var count = sb.Length - 1;
    for (int i = 0; i < count; i++)
    {
        var k = count - i;
        var swap = sb[i];
        sb[i] = sb[k];
        sb[k] = swap;
    }

    if (isNegative)
    {
        sb.Insert(0, '-');
    }

    if (decimalPart == 0D)
    {
        return new StringPrimitive(sb.ToString());
    }

    var runningValue = 0D;
    var decimalIndex = 1D;
    var decimalTemp = decimalPart;

    sb.Append('.');
    while (decimalIndex < 100 && decimalPart - runningValue > 1.0e-50)
    {
        var result = decimalTemp * radix;
        var integralResult = Math.Truncate(result);
        runningValue += integralResult / Math.Pow(radix, decimalIndex++);
        decimalTemp = result - integralResult;
        sb.Append(radixChars[(int)integralResult]);
    }

    return new StringPrimitive(sb.ToString());
}

هل يمكن لأي شخص لديه خبرة أكبر في البرمجة ذات المستوى المنخفض أن يشرح السبب dtoa.c لديه ما يقرب من 40 ضعف رمز؟ لا أستطيع أن أتخيل أن C# أكثر إنتاجية.

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

المحلول

يحتوي DTOA.C على وظيفتين رئيسيتين: DTOA () ، والذي يحول مزدوجًا إلى سلسلة ، و strtod () ، والذي يحول سلسلة إلى مزدوجة. كما أنه يحتوي على الكثير من وظائف الدعم ، معظمها لتنفيذها الخاص للحساب التعسفي. إن مطالبة DTOA.C بالشهرة هي الحصول على هذه التحويلات بشكل صحيح ، وهذا لا يمكن القيام به ، بشكل عام ، بحساسية تعسفية. كما أن لديها رمزًا لتحويل التحويلات بشكل صحيح في أربعة أوضاع مختلفة.

يحاول الكود الخاص بك فقط تنفيذ ما يعادل DTOA () ، وبما أنه يستخدم نقطة العائمة لإجراء تحويلاتها ، فلن يحصل عليها دائمًا. (تحديث: انظر مقالتي http://www.exploringbinary.com/quick-and-dirty-point-to--decimal-conversion/ للتفاصيل.)

(لقد كتبت الكثير عن هذا على مدونتي ، http://www.exploringbinary.com/ . ستة من آخر مقالاتي السبعة كانت حول تحويلات strtod () وحدها. اقرأهم لمعرفة مدى تعقيد التحويلات المستديرة بشكل صحيح.)

نصائح أخرى

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

المصدر الرئيسي للصعوبة هو أن العديد من الكسور العشرية ، حتى البسيطة ، لا يمكن أن تكون بدقة معبراً عنه باستخدام نقطة عائمة ثنائية - على سبيل المثال ، 0.5 يمكن (من الواضح) ، ولكن 0.1 لا تستطيع. والذهاب في الاتجاه الآخر (من الثنائي إلى العشري) ، لا تريد عمومًا النتيجة الدقيقة تمامًا (على سبيل المثال ، القيمة العشرية الدقيقة لأقرب الرقم إلى 0.1 التي يمكن تمثيلها في IEEE-754 متوافقة double هو في الواقع0.1000000000000000055511151231257827021181583404541015625) لذلك تريد عادة بعض التقريب.

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

ألق نظرة على الورقة المذكورة في التعليق في الجزء العلوي من dtoa.c التنفيذ ، كلنجر كيفية قراءة أرقام النقطة العائمة بدقة, ، لنكهة المشكلة ؛ وربما ورقة ديفيد م. جاي (المؤلف) ، تحويلات ثنائية وعشرية مدونة بشكل صحيح.

(أيضًا ، بشكل عام: ما يجب أن يعرفه كل عالم كمبيوتر عن الحساب العائم.)

استنادًا إلى نظرة سريعة عليها ، يتم التعامل مع قدر لا بأس به من إصدار C ؛ مع طن من #define التكوين.

أعتقد أيضًا أن الكود في dtoa.c قد يكون أكثر كفاءة (مستقلة عن اللغة). على سبيل المثال ، يبدو أنه يقوم ببعض الدعوى ، وهو ما يعني في أيدي الخبير السرعة غالبًا. أفترض أنها ببساطة تستخدم خوارزمية أقل سهولة لأسباب السرعة.

إجابة قصيرة: لأن dtoa.c يعمل.

هذا هو بالضبط الفرق بين المنتج الذي تم تنشيطه جيدًا ونموذج NIH.

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