دعونا نلقي نظرة على سؤالين أولاً:
0.1 + 0.2 == 0.3 ؛ // خطأ شنيع
999999999999999999 == 100000000000000000 ؛ // حقيقي
المشكلة الأولى هي دقة العشرية ، التي تمت مناقشتها في العديد من المدونات في الصناعة. كانت المشكلة الثانية هي أنه في العام الماضي ، عندما كانت قاعدة بيانات منهجية تصحيح البيانات ، وجدت أن بعض البيانات قد تم تكرارها. ستبدأ هذه المقالة من القاعدة وتلخيص القضايا المذكورة أعلاه.
الحد الأقصى عدد صحيح
يتم تخزين الأرقام الموجودة في JavaScript باستخدام أرقام نقاط عائمة 64 بت دقة 64 بت ، وتنسيقها هو:
sxmx 2^e
S هو جزء علامة ، مما يشير إلى إيجابية وسلبية. M هو Mantissa ، مع 52 بت. ه هو الأسس ، مع 11 بت. في مواصفات ECMASCRIPT ، النطاق المقدم من E هو [-1074 ، 971]. وبهذه الطريقة ، من السهل استنتاج أن الحد الأقصى لعدد صحيح يمكن أن يمثله JavaScript هو:
1 x (2^53 - 1) x 2^971 = 1.7976931348623157e+308
هذه القيمة هي الرقم. max_value
وبالمثل ، يمكن استنتاج قيمة الرقم. min_value على النحو التالي:
1 x 1 x 2^(-1074) = 5e-324
لاحظ أن min_value يمثل الرقم الإيجابي الأقرب إلى 0 ، وليس أصغر رقم. أصغر رقم هو -number.max_value
تضيع دقة العشرية
أرقام JavaScript هي أرقام عائمة الدقة المزدوجة ويتم تخزينها في أجهزة الكمبيوتر الثنائية. عندما يتجاوز عدد البتات المهمة 52 بت ، سيكون هناك فقدان الدقة. على سبيل المثال:
الثنائي العشري 0.1 هو 0.0 0011 0011 0011 ... (الحلقة 0011)
الثنائي العشري 0.2 هو 0.0011 0011 0011 ... (الحلقة 0011)
يمكن التعبير عن الإضافة 0.1 + 0.2 على النحو التالي:
E = -4 ؛ م = 1.10011001100 ... 1100 (52 بت)
+ e = -3 ؛ م = 1.10011001100 ... 1100 (52 بت)
------------------------------------------------------------------------------------------------------------------------------------
E = -3 ؛ M = 0.11001100110 ... 0110
+ e = -3 ؛ م = 1.10011001100 ... 1100
------------------------------------------------------------------------------------------------------------------------------------
E = -3 ؛ M = 10.01100110011 ... 001
------------------------------------------------------------------------------------------------------------------------------------
= 0.01001100110011 ... 001
= 0.300000000000000004 (عشري)
استنادًا إلى الحساب أعلاه ، يمكننا أيضًا استنتاج: عندما لا يتجاوز العدد المحدود من العشرية العشرية في التمثيل الثنائي 52 بت ، يمكن تخزينه بدقة في JavaScript. على سبيل المثال:
0.05 + 0.005 == 0.055 // true
قواعد أخرى ، مثل:
0.05 + 0.2 == 0.25 // true
0.05 + 0.9 == 0.95 // false
يجب أن يتم النظر في أوضاع التقريب من IEEE 754 ، ويمكن للمهتمين أكثر دراستها.
تضيع دقة الأعداد الصحيحة الكبيرة
نادراً ما يتم ذكر هذا السؤال. أولاً ، علينا معرفة ما هي المشكلة:
1. ما هو الحد الأقصى لعدد صحيح يمكن أن تخزنه جافا سكريبت؟
تم الإجابة على هذا السؤال سابقًا ، وهو رقم. max_value ، رقم كبير جدًا.
2. ما هو الحد الأقصى لعدد صحيح يمكن أن يخزنه جافا سكريبت دون فقدان الدقة؟
وفقًا لـ SXMX 2^e ، تكون بت الإشارة إيجابية ، فإن mantissa 52 بت مبطنة تمامًا مع 1 ، والأسس E هو القيمة القصوى 971. من الواضح أن الإجابة لا تزال. max_value.
ما هي بالضبط مشكلتنا؟ ارجع إلى رمز البداية:
999999999999999999 == 100000000000000000 ؛ // حقيقي
من الواضح أن 16 9s أقل بكثير من 308 10s. هذه المشكلة لا علاقة لها بـ Max_Value ، ويجب أن تعزى إلى Mantis M مع 52 رقمًا فقط.
يمكن وصفه في الكود:
var x = 1 ؛ // من أجل تقليل مبلغ الحساب ، يمكن تعيين القيمة الأولية لتكون أكبر ، مثل Math.Pow (2 ، 53) - 10
بينما (x! = x+1) x ++ ؛
// x = 9007199254740992 أي 2^53
وهذا يعني ، عندما يكون x أقل من أو يساوي 2^53 ، يتم التأكد من عدم فقدان دقة x. عندما تكون x أكبر من 2^53 ، قد تضيع دقة x. على سبيل المثال:
عندما يكون x 2^53 + 1 ، فإن تمثيله الثنائي هو:
100000000000 ... 001 (هناك 52 0s في الوسط)
عند التخزين بأرقام نقاط عائمة دقة مزدوجة:
ه = 1 ؛ M = 10000..00 (إجمالي 52 0s ، 1 مخفي بت)
من الواضح أن هذا هو نفس التخزين 2^53.
وفقًا للفكرة أعلاه ، يمكن تحديد أنه بالنسبة إلى 2^53 + 2 ، فإن ثنائيه هو 100000 ... 0010 (51 0s في الوسط) ، ويمكن أيضًا تخزينه بدقة.
القاعدة: عندما يكون X أكبر من 2^53 وعدد الأرقام الثنائية الهامة أكبر من 53 بت ، سيكون هناك فقدان الدقة. هذا هو في الأساس نفس فقدان دقة العشرية.
يمكن استخدام بت مخفي للرجوع إليها: برنامج تعليمي حول نوع Java المزدوج.
ملخص
دقة العشرية والأعداد الصحيحة الكبيرة لا تضيع فقط في جافا سكريبت. بالمعنى الدقيق للكلمة ، فإن أي لغة برمجة (C/C ++/C#/Java ، وما إلى ذلك) تستخدم تنسيق رقم النقطة العائمة IEEE 754 لتخزين أنواع النقاط العائمة مشكلات فقدان الدقة. في C# و Java ، يتم توفير فئات التغليف العشري والبيئة لأداء المعالجة المقابلة ، وذلك لتجنب فقدان الدقة.
ملاحظة: هناك بالفعل اقتراح عشري في مواصفات ECMASCRIPT ، ولكن لم يتم اعتماده رسميًا بعد.
أخيرًا ، اختبر الجميع:
number.max_value + 1 == numer.max_value ؛
number.max_value + 2 == numer.max_value ؛
...
number.max_value + x == numer.max_value ؛
number.max_value + x + 1 == Infinity ؛
...
number.max_value + number.max_value == Infinity ؛
// سؤال:
// 1. ما هي قيمة x؟
// 2. Infinity - number.max_value == x + 1 ؛ صحيح أم خطأ؟
إن المناقشة الموجزة أعلاه حول فقدان دقة العشرية والأعداد الصحيحة الكبيرة في JavaScript هي كل المحتوى الذي أشاركه معك. آمل أن تتمكن من إعطائك مرجعًا وآمل أن تتمكن من دعم wulin.com أكثر.