هل هناك طريقة للقيام "الصحيح" بالتقريب الحسابي في .NET؟ / ج#

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

سؤال

أحاول أن أحصل على رقم إلى مكانه العشري الأول ، وبالنظر إلى الخيارات المتوسطة المختلفة ، يبدو أنه يعمل بشكل جيد. تنشأ مشكلة على الرغم من أن هذا الرقم يحتوي على أماكن عشرية عشرية من شأنها أن تؤثر بشكل حسابي على التقريب.

مثال:

مع 0.1, 0.11..0.19 و 0.141..0.44 إنها تعمل:

Math.Round(0.1, 1) == 0.1
Math.Round(0.11, 1) == 0.1
Math.Round(0.14, 1) == 0.1
Math.Round(0.15, 1) == 0.2
Math.Round(0.141, 1) == 0.1

ولكن مع 0.141..0.149 يعود دائما 0.1, ، برغم من 0.146..0.149 يجب أن تدور إلى 0.2:

Math.Round(0.145, 1, MidpointRounding.AwayFromZero) == 0.1
Math.Round(0.146, 1, MidpointRounding.AwayFromZero) == 0.1
Math.Round(0.146, 1, MidpointRounding.ToEven) == 0.1
Math.Round(0.146M, 1, MidpointRounding.ToEven) == 0.1M
Math.Round(0.146M, 1, MidpointRounding.AwayFromZero) == 0.1M

حاولت الخروج بوظيفة تعالج هذه المشكلة ، وهي تعمل بشكل جيد لهذه الحالة ، لكنها بالطبع تفشل ببراعة إذا حاولت أن تدور حول أي 0.144449 إلى أول رقم عشري (يجب أن يكون 0.2, ، لكن النتائج 0.1.) (هذا لا يعمل مع Math.Round () أيضًا.)

private double "round"(double value, int digit)
{
    // basically the old "add 0.5, then truncate to integer" trick
    double fix = 0.5D/( Math.Pow(10D, digit+1) )*( value >= 0 ? 1D : -1D );
    double fixedValue = value + fix;

    // 'truncate to integer' - shift left, round, shift right
    return Math.Round(fixedValue * Math.Pow(10D, digit)) / Math.Pow(10D, digit);
}

أفترض أن الحل هو تعداد جميع الأرقام ، والعثور على القيمة الأولى أكبر من 4 ثم تدور لأعلى ، أو حولها لأسفل. المشكلة 1: هذا يبدو أحمق ، المشكلة 2: ليس لدي أي فكرة عن كيفية تعداد الأرقام دون غزليون من الضربات وطرحها.

قصة قصيرة طويلة: ما هي أفضل طريقة للقيام بذلك؟

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

المحلول

Math.Round() يتصرف بشكل صحيح.

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

الفكرة مع تقريب نقطة الوسط هي أن نصف الأرقام الفاصلة يجب أن يتجول ويجب أن يتجول نصفهم. لذلك بالنسبة للأرقام بين 0.1 و 0.2 ، يجب أن يدور نصفها إلى 0.1 ونصف أن يدور إلى 0.2. نقطة الوسط بين هذين الرقمين هي 0.15 ، لذلك هذا هو العتبة للتجول. 0.146 أقل من 0.15 ، لذلك يجب أن يدور إلى 0.1.

                    Midpoint
0.10                  0.15                  0.20
 |----------------|----|---------------------|
                0.146
       <---- Rounds Down

نصائح أخرى

لا أحصل على ما تحاول إنجازه هنا. 0.149 مدور إلى مكان واحد عشري هو 0.1 ، وليس 0.2

التقريب ليست عملية تكرارية ، فأنت تدور مرة واحدة فقط.

لذلك 0.146 مدور إلى 1 رقم عشري هو 0.1.

أنت لا تفعل هذا:

0.146 --> 0.15
0.15 -->  0.2

أنت تفعل هذا فقط:

0.146 --> 0.1

خلاف ذلك ، ما يلي:

0.14444444444444446

سوف يدور أيضًا إلى 0.2 ، لكنه لا ، ولا ينبغي.

لا تحاول مضاعفة "أخطاء" التقريب. وهو ما تحاول القيام به.

.146 ينبغي جولة إلى .1 إذا كنت ستذهب إلى مكان عشري واحد.

من خلال تقريبه إلى .15 أولاً ، ثم مرة أخرى إلى .2 ، فأنت فقط تقدم المزيد من الخطأ الدائري ، وليس أقل.

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