سؤال

float f = 0.7;
if( f == 0.7 )
    printf("equal");
else
    printf("not equal");

لماذا الإخراج not equal ?

لماذا يحدث هذا؟

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

المحلول

يحدث هذا لأنه في بيانك

  if(f == 0.7)

يتم التعامل مع 0.7 كمضاعفة. حاول 0.7F لضمان التعامل مع القيمة كطويلة:

  if(f == 0.7f)

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

نصائح أخرى

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

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

جميع الأرقام غير الصحيحة التي يمكن تمثيلها بالضبط 5 كما هو آخر رقم عشري. لسوء الحظ، العكس ليس صحيحا: بعض الأرقام لديها 5 كأفضل رقم عشري آخر ولا يمكن تمثيلها بالضبط. يمكن تمثيل الأعداد الصحيحة الصغيرة جميعها بالضبط، وتنفذ تقسيم قوة 2 رقما يمكن تمثيله بآخر يمكن تمثيله، طالما أنك لا تدخل عالم الأرقام الشورمية.

بادئ ذي بدء، دعنا ننظر داخل رقم التعويم. أنا آخذ 0.1F هو 4 بايت طويلة (ثنائي 32)، في عرافة هو
3D CC CC CD.
من خلال Standart IEEE 754 لتحويله إلى عشري يجب أن نفعل مثل هذا:

enter image description here
في القرص المضغوط الثنائي 3D CC CC
0 01111011 1001100 11001100 11001101
هنا الرقم الأول هو توقيع قليلا. 0 يعني (-1) ^ 0 أن عددنا إيجابي.
الثانية 8 بت كبير. في ثنائي، هو 01111011 - في عشري 123. ولكن الأسترال الحقيقي هو 123-127 (دائما 127) =-4, ، يعني أننا نحتاج إلى ضرب الرقم الذي سنحصل عليه بحلول 2 ^ (- 4).
آخر 23 بايت هو الدقة الهادفة. هناك feel the first the fee 1 / (2 ^ 1) (0.5)، ثانية بنسبة 1 / (2 ^ 2) (0.25) وهلم جرا. هنا ما نحصل عليه:


enter image description here enter image description here

نحتاج إلى إضافة جميع الأرقام (الطاقة 2) وإضافة إلى ذلك 1 (دائما 1، عن طريق ستاندارت). أنه
1,60000002384185791015625
الآن دعنا نضرب هذا الرقم بحلول 2 ^ (- 4)، إنه من الأس. نحن فقط تراجع الرقم أعلاه بنسبة 2 أربعة مرات:
0,100000001490116119384765625
اعتدت MS حاسبة


**

الآن الجزء الثاني. تحويل من عشري إلى ثنائي.

**
أنا آخذ الرقم 0.1
إنها سهولة لأنه لا يوجد جزء صحيح. قم بتسجيل الدخول الأول - إنه 0. الدقة الراهبة والأهمية التي سأحسبها الآن. يتضاعف المنطق برقم كامل (0.1 * 2 = 0.2) وإذا كان أكبر من 1 محاكاة ومتابعة.
enter image description here
والرقم هو .00011001100110011001100110011، تقول ستاندارت إنه يجب علينا التحول إلى اليسار قبل أن نحصل على 1. (شيء). كيف ترى نحن بحاجة إلى 4 نوبات، من هذا الرقم حساب الأسهم(127-4=123). والأهمية الدقة الآن
10011001100110011001100(وهناك ضائعة بت).
الآن العدد كله. تسجيل بت 0 الأسهم هو 123 (01111011) وتحمل الدقة هو 10011001100110011001100 وكامل هو
00111101110011001100110011001100 دعنا نقارنه مع أولئك الذين لدينا من الفصل السابق
00111101110011001100110011001101
كما ترى القليل قليلا ليست متساوية. ذلك لأنني اقتطاع الرقم. يعرف وحدة المعالجة المركزية والمترجم أن هذا شيء بعد الدقة الرفيعة لا يمكن أن يحمل وتوضع فقط بت الأخير إلى 1.

المشكلة التي تواجهها هي أن المشكلات التي تواجهها، كما لاحظ المعلقون الآخرون، أنه غير آمن عموما لاختبار التعادل الدقيق بين العوامات، كأخطاء التهيئة، أو أخطاء التقريب في الحسابات يمكن أن يعرض اختلافات طفيفة تسبب == المشغل لإرجاع FALSE.

ممارسة أفضل هي أن تفعل شيئا مثل

float f = 0.7;
if( fabs(f - 0.7) < FLT_EPSILON )
    printf("equal");
else
    printf("not equal");

على افتراض أنه تم تعريف FLT_EPSILON كقيمة تعويم صغيرة بشكل مناسب لمنصتك.

نظرا لأن أخطاء التقريب أو التهيئة من غير المرجح أن تتجاوز قيمة FLT_EPSILON، فسيمنحك هذا اختبار المكافئ الموثوق الذي تبحث عنه.

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

      // Floating point comparison:

        bool CheckFP32Equal(float referenceValue, float value)
        {
           const float fp32_epsilon = float(1E-7);
           float abs_diff = std::abs(referenceValue - value);

           // Both identical zero is a special case
           if( referenceValue==0.0f && value == 0.0f)
              return true;

           float rel_diff = abs_diff / std::max(std::abs(referenceValue) , std::abs(value) ); 

           if(rel_diff < fp32_epsilon)
                 return true;
           else 
                 return false;

        }

تم ربط سؤال دقيق آخر بالضبط بهذا السبب في وقت متأخر من السنتين. لا أعتقد أن الإجابات المذكورة أعلاه كاملة.

int fun1 ( void )
{
      float x=0.7;
      if(x==0.7) return(1);
      else       return(0);
}
int fun2 ( void )
{
      float x=1.1;
      if(x==1.1) return(1);
      else       return(0);
}
int fun3 ( void )
{
      float x=1.0;
      if(x==1.0) return(1);
      else       return(0);
}
int fun4 ( void )
{
      float x=0.0;
      if(x==0.0) return(1);
      else       return(0);
}
int fun5 ( void )
{
      float x=0.7;
      if(x==0.7f) return(1);
      else       return(0);
}
float fun10 ( void )
{
    return(0.7);
}
double fun11 ( void )
{
    return(0.7);
}
float fun12 ( void )
{
    return(1.0);
}
double fun13 ( void )
{
    return(1.0);
}

Disassembly of section .text:

00000000 <fun1>:
   0:   e3a00000    mov r0, #0
   4:   e12fff1e    bx  lr

00000008 <fun2>:
   8:   e3a00000    mov r0, #0
   c:   e12fff1e    bx  lr

00000010 <fun3>:
  10:   e3a00001    mov r0, #1
  14:   e12fff1e    bx  lr

00000018 <fun4>:
  18:   e3a00001    mov r0, #1
  1c:   e12fff1e    bx  lr

00000020 <fun5>:
  20:   e3a00001    mov r0, #1
  24:   e12fff1e    bx  lr

00000028 <fun10>:
  28:   e59f0000    ldr r0, [pc]    ; 30 <fun10+0x8>
  2c:   e12fff1e    bx  lr
  30:   3f333333    svccc   0x00333333

00000034 <fun11>:
  34:   e28f1004    add r1, pc, #4
  38:   e8910003    ldm r1, {r0, r1}
  3c:   e12fff1e    bx  lr
  40:   66666666    strbtvs r6, [r6], -r6, ror #12
  44:   3fe66666    svccc   0x00e66666

00000048 <fun12>:
  48:   e3a005fe    mov r0, #1065353216 ; 0x3f800000
  4c:   e12fff1e    bx  lr

00000050 <fun13>:
  50:   e3a00000    mov r0, #0
  54:   e59f1000    ldr r1, [pc]    ; 5c <fun13+0xc>
  58:   e12fff1e    bx  lr
  5c:   3ff00000    svccc   0x00f00000  ; IMB

لماذا ترجع Fun3 و Fun4 واحدا وليس الآخرين؟ لماذا يعمل Fun5؟

إنه يتعلق بالغة. تقول اللغة أن 0.7 مزدوجة إلا إذا كنت تستخدم بناء الجملة هذا 0.7F، فهذا هو واحد. وبالتالي

  float x=0.7;

يتم تحويل 0.7 المزدوج إلى واحدة ومخزنة في X.

  if(x==0.7) return(1);

تقول اللغة إن علينا الترويج لها بدقة أعلى حتى يتم تحويل الواحد في X إلى مضاعفة ومقارنة مع 0.7 المزدوج.

00000028 <fun10>:
  28:   e59f0000    ldr r0, [pc]    ; 30 <fun10+0x8>
  2c:   e12fff1e    bx  lr
  30:   3f333333    svccc   0x00333333

00000034 <fun11>:
  34:   e28f1004    add r1, pc, #4
  38:   e8910003    ldm r1, {r0, r1}
  3c:   e12fff1e    bx  lr
  40:   66666666    strbtvs r6, [r6], -r6, ror #12
  44:   3fe66666    svccc   0x00e66666

واحد 3F333333 مزدوج 3fe66666666666666

كما أشار أليكساندر إذا ظل هذا الإجابة IEEE 754

seeeeeeeeffffffffffffffffffff.

ومضاعف

seeeeeeeeeeeefffffffffffffffffffffffffffffffffffffffff.

مع 52 بت من الكسر بدلا من 23 هذا واحد لديه.

00111111001100110011... single
001111111110011001100110... double

0 01111110 01100110011... single
0 01111111110 01100110011... double

تماما مثل 1/3 في قاعدة 10 هو 0.3333333 ... إلى الأبد. لدينا نمط متكرر هنا 0110

01100110011001100110011 single, 23 bits
01100110011001100110011001100110.... double 52 bits.

وهنا هو الجواب.

  if(x==0.7) return(1);

يحتوي X على 01100110011001100110011 كجزء كبير، عندما يتم تحويل ذلك مرة أخرى لمضاعفة الكسر

01100110011001100110011000000000....

وهو ما لا يساوي

01100110011001100110011001100110...

لكن هنا

  if(x==0.7f) return(1);

لا يحدث هذا الترويج أن أنماط بت نفسها مقارنة مع بعضها البعض.

لماذا 1.0 العمل؟

00000048 <fun12>:
  48:   e3a005fe    mov r0, #1065353216 ; 0x3f800000
  4c:   e12fff1e    bx  lr

00000050 <fun13>:
  50:   e3a00000    mov r0, #0
  54:   e59f1000    ldr r1, [pc]    ; 5c <fun13+0xc>
  58:   e12fff1e    bx  lr
  5c:   3ff00000    svccc   0x00f00000  ; IMB

0011111110000000...
0011111111110000000...

0 01111111 0000000...
0 01111111111 0000000...

في كلتا الحالتين الكسر هو كل الأصفار. لذلك التحويل من مزدوج إلى واحد لمضاعفة لا يوجد فقدان الدقة. يتحول من واحدة إلى ضعف مقارنة القيم بالضبط بالضبط.

أعلى إجابة صوتية وفحصها Halfdan هي الإجابة الصحيحة، هذه هي حالة من الدقة المختلطة ويجب ألا تفعل مقارنة متساوين.

لماذا لم يظهر في هذه الإجابة. 0.7 فشل 1.0 يعمل. لماذا لم تظهر 0.7 فشل. السؤال المكرر 1.1 يفشل كذلك.


تعديل

يمكن إخراج المساواة من هذه المشكلة هنا، فهو سؤال مختلف تم الرد عليه بالفعل، لكنه نفس المشكلة وأيضا لديه "ما ..." الصدمة الأولية.

int fun1 ( void )
{
      float x=0.7;
      if(x<0.7) return(1);
      else       return(0);
}
int fun2 ( void )
{
      float x=0.6;
      if(x<0.6) return(1);
      else       return(0);
}

Disassembly of section .text:

00000000 <fun1>:
   0:   e3a00001    mov r0, #1
   4:   e12fff1e    bx  lr

00000008 <fun2>:
   8:   e3a00000    mov r0, #0
   c:   e12fff1e    bx  lr

لماذا يعرض واحد أقل من والآخر لا يقل عن؟ عندما يجب أن تكون متساوية.

من أعلى نعرف قصة 0.7.

01100110011001100110011 single, 23 bits
01100110011001100110011001100110.... double 52 bits.

01100110011001100110011000000000....

اقل من.

01100110011001100110011001100110...

0.6 هو نمط متكرر مختلف 0011 بدلا من 0110.

ولكن عند تحويلها من مزدوج إلى واحدة أو بشكل عام عند تمثيلها كواحد من IEEE 754.

00110011001100110011001100110011.... double 52 bits.
00110011001100110011001 is NOT the fraction for single
00110011001100110011010 IS the fraction for single

يستخدم IEEE 754 أوضاع التقريب أو تقريب أو جولة أو جولة إلى صفر. المبرمات تميل إلى الجولة افتراضيا. إذا كنت تتذكر التقريب في المدرسة الدراسية 12345678 إذا كنت أرغب في الجولة إلى الرقم الثالث من الأعلى، فستكون 12300000 ولكن جولة إلى الرقم التالي 1235000 إذا كان الرقم بعد 5 أو أكبر ثم تقريب. 5 هو 1/2 من 10 الأساس (العشري) في ثنائي 1 هو 1/2 من القاعدة، لذلك إذا كان الرقم بعد الموقف الذي نريد جولة هو 1 ثم تقريب آخر لا. لذلك لمدة 0.7 لم ندخل، لأننا نفعل 0.6 جولة.

والآن من السهل أن نرى ذلك

00110011001100110011010

تحويلها إلى ضعف بسبب (x <0.7)

00110011001100110011010000000000....

أكبر من

00110011001100110011001100110011....

لذلك دون الحاجة إلى التحدث عن استخدام المساواة، لا تزال المشكلة لا تزال تقدم نفسها 0.7 هو مزدوج 0.7F هو واحد، يتم الترويج للعملية إلى أعلى دقة إذا كانت تختلف.

النظر في هذا:

int main()
{
    float a = 0.7;
    if(0.7 > a)
        printf("Hi\n");
    else
        printf("Hello\n");
    return 0;
}

إذا (0.7> أ) هنا هو متغير تعويم و 0.7 هو ثابت مزدوج. ثابت مزدوج 0.7 أكبر من متغير تعويم أ. وبالتالي فإن الحالة إذا كانت راضية وطبعها 'Hi'

مثال:

int main()
{
    float a=0.7;
    printf("%.10f %.10f\n",0.7, a);
    return 0;
}

انتاج:
0.7000000000 0.6999999881

إذا قمت بتغيير نوع البيانات F ل مزدوج, ، سوف تطبع مساو, ، هذا بسبب الثوابت في النقطة العائمة المخزنة في مزدوج وغير العائمة في طويل, ، الدقة المزدوجة عالية وتطفو لها قيمة أقل دقة، وقيمة مزدوجة مخزنة في 64 قيمة البتة الثنائية والطفو المخزنة في 32 Bit Binary، سيكون من الواضح تماما إذا رأيت طريقة تحويل أرقام النقطة العائمة إلى التحويل الثنائي.

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