Image - www.freepik.com
منذ عدة سنوات فكرت وكتبت الكثير عن رياضيات النقطة العائمة. كان الأمر ممتعًا للغاية ، وفي عملية البحث ، تعلمت الكثير ، لكن في بعض الأحيان لم أستخدم كل هذه المهارات في الممارسة العمليةلوقت طويل. لذلك ، أنا مسرور للغاية في كل مرة يجب أن أعمل فيها على خلل يتطلب معرفة متخصصة متنوعة. في هذه المقالة ، سأروي ثلاث قصص عن أخطاء النقطة العائمة التي تعلمتها في Chromium.
الجزء الأول: توقعات غير واقعية
تم استدعاء الخطأ "JSON لا يحلل عدد صحيح من 64 بت بشكل صحيح" ؛ لا يبدو الأمر وكأنه نقطة عائمة أو مشكلة في المتصفح في البداية ، ولكن تم نشره على موقع crbug.com لذلك طُلب مني إلقاء نظرة. أسهل طريقة لإعادة إنشائها هي فتح أدوات مطور Chrome (F12 أو Ctrl + Shift + I) ولصق الكود التالي في وحدة تحكم المطور:
json = JSON.parse(‘{“x”: 2940078943461317278}’); alert(json[‘x’]);
يُعد إدخال رمز غير معروف في نافذة وحدة التحكم طريقة رائعة للاختراق ، ولكن الرمز كان بسيطًا جدًا لدرجة أنني تمكنت من اكتشاف أنه لم يكن ضارًا. في تقرير الخطأ ، أشار المؤلف إلى توقعاته والنتائج الفعلية:
ما هو السلوك المتوقع؟ يجب إرجاع قيمة عدد صحيح 2940078943461317278
ما هو الخطأ؟ يتم إرجاع عدد صحيح 2940078943461317000 بدلاً من ذلك.
تم العثور على "الخطأ" في Linux ، وأنا أعمل على Chrome لنظام التشغيل Windows ، ولكن هذا السلوك عبر الأنظمة الأساسية ، ولدي معرفة بأرقام الفاصلة العائمة ، لذلك بحثت عنها.
من المحتمل أن يكون هذا السلوك للأعداد الصحيحة خطأً في النقطة العائمة ، لأنه لا يوجد نوع عدد صحيح في JavaScript. وللسبب نفسه ، هذا ليس خطأ في الواقع.
الرقم الذي تم إدخاله كبير جدًا ، فهو يساوي تقريبًا 2.9e18. وهذه هي المشكلة. نظرًا لأن JavaScript لا يحتوي على نوع عدد صحيح ، فإنه يستخدم الدقة المزدوجة للفاصلة العائمة IEEE-754 للأرقام . يحتوي تنسيق النقطة العائمة الثنائية هذا على بت إشارة ، وأس 11 بت ، و 53 بت الجزء العشري (نعم ، هذا 65 بت ، بت واحد مخفي بالسحر). هذا النوع المزدوج جيد جدًا في تخزين الأعداد الصحيحة لدرجة أن العديد من مبرمجي JavaScript لم يلاحظوا أبدًا عدم وجود نوع عدد صحيح. ومع ذلك ، فإن أعدادًا كبيرة جدًا تدمر هذا الوهم.
يمكن لرقم JavaScript تخزين أي قيمة عدد صحيح يصل إلى 2 ^ 53 بدقة. بعد ذلك ، يمكنه تخزين جميع الأرقام الزوجية حتى 2 ^ 54. وبعد ذلك يمكنه تخزين جميع مضاعفات أربعة أعداد حتى 2 ^ 55 ، وهكذا.
يتم التعبير عن رقم المشكلة في التدوين الأسي للأساس 2 ، والذي يساوي تقريبًا 1.275 * 2 ^ 61. يمكن التعبير عن عدد قليل جدًا من الأعداد الصحيحة في هذه الفترة الزمنية - المسافة بين الأرقام هي 512. فيما يلي الأرقام الثلاثة المقابلة:
- 2 94078943 461317278 هو الرقم الذي أراد كاتب تقرير الخطأ الاحتفاظ به
- 294078943461317120 - ضعف الأقرب لهذا الرقم (أقل منه)
- 294078943461317632 - أقرب رقم مزدوج (أكبر منه)
الرقم الذي نحتاجه موجود في الفترة الفاصلة بين هذين الزوجين ووحدة JSON (على سبيل المثال ، JavaScript نفسها أو أي وظيفة أخرى تم تنفيذها بشكل صحيح لتحويل النص إلى مضاعفة) قامت بأفضل ما لديها وأعادت أقرب مضاعفة. ببساطة ، لا يمكن تخزين الرقم الذي أراد مؤلف التقرير حفظه في النوع الرقمي المدمج في JavaScript .
حتى الآن ، كل شيء واضح: إذا وصلت إلى حدود اللغة ، فأنت بحاجة إلى معرفة المزيد حول كيفية عملها. لكن لا يزال هناك لغز آخر. يشير تقرير الخطأ إلى أنه تم إرجاع الرقم التالي في الواقع:
294078943461317000
الموقف مثير للفضول ، لأن هذا ليس رقمًا تم إدخاله ، وليس أقرب رقم مزدوج ، وفي الواقع ، ليس حتى رقمًا يمكن تمثيله على أنه رقم مزدوج!
يتم شرح هذا اللغز أيضًا من خلال مواصفات JavaScript. تشير المواصفات إلى أنه عند طباعة رقم ، يجب أن ينتج التطبيق عددًا كافيًا من الأرقام لتحديده بشكل فريد ، وليس أكثر. هذا مفيد لطباعة أرقام مثل 0.1 ، والتي لا يمكن تمثيلها بدقة على أنها مضاعفة. على سبيل المثال ، إذا تطلب JavaScript 0.1 ليتم إخراجها كقيمة مخزنة ، فسيتم إخراج:
0.1000000000000000055511151231257827021181583404541015625
ستكون نتيجة دقيقة ، لكنها ستربك الناس بعدم إضافة أي شيء مفيد. يمكن العثور على قواعد محددة هنا (ابحث عن السطر "ToString المطبق على نوع الرقم"). لا أعتقد أن المواصفات تتطلب تتبع الأصفار ، لكنها بالتأكيد تتطلب ذلك.
لذلك ، عند تشغيل البرنامج ، ينتج JavaScript 2،940،078،943،461،317،000 للأسباب التالية:
- فقدت قيمة الرقم الأصلي عند حفظه كرقم JavaScript
- الرقم المعروض قريب بدرجة كافية من القيمة المخزنة للتعرف عليه بشكل فريد
- الرقم المعروض هو أبسط رقم يعرف القيمة المخزنة بشكل فريد
كل شيء يعمل كما ينبغي ، هذا ليس خطأ ، تم إغلاق المشكلة مثل WontFix ("غير قابل للاسترداد"). يمكن العثور على الخطأ الأصلي هنا .
الجزء 2: إبسيلون سيئ
هذه المرة أصلحت الخطأ ، أولاً في Chromium ثم في googletest ، لتجنب الارتباك للأجيال القادمة من المطورين.

كان هذا الخطأ فشل اختبار غير حتمي بدأ يحدث فجأة. نحن نكره إخفاقات الاختبار الغامضة هذه. إنهم مربكون بشكل خاص عندما يبدأون في إجراء اختبار لم يتغير منذ سنوات. بعد بضعة أسابيع ، تم استدعائي للتحقيق. بدأت رسائل الخطأ (المعدلة قليلاً لأطوال الخطوط) على النحو التالي:
الفرق بين الميكروثانية المتوقعة والميكروثانية المحولة هو 512 ، وهو ما يتجاوز 1.0 [الفرق بين الميكروثانية المتوقعة والميكروثانية المحولة هو 512 ، والذي يتجاوز 1.0]
نعم ، هذا يبدو سيئا. هذه رسالة خطأ في googletest تقول أن قيمتي الفاصلة العائمة اللتين يجب ألا تكون بينهما أكثر من 1.0 تفصل بينهما في الواقع 512. وكان
أول دليل هو الفرق بين أرقام الفاصلة العائمة. بدا مريبًا جدًا أن يتم الفصل بين الرقمين بـ 2 ^ 9 بالضبط. صدفة؟ لا أعتقد ذلك. أقنعني ما تبقى من المنشور ، والذي أشار إلى القيمتين اللتين تتم مقارنتهما ، بالسبب:
يُقَوِّم توقع_ميكروثانية إلى 4.2934311416234112e + 18 ،
ويتم تقييم المحول_ميكرو ثانية إلى 4.2934311416234107e + 18
إذا كنت قد قاتلت مع IEEE 754 لفترة كافية ، فستفهم على الفور ما يحدث.
لقد قرأت الجزء الأول ، لذا يمكنك أن تشعر بـ déjà vu بسبب نفس الأرقام. ومع ذلك ، فهذه محض صدفة - أنا فقط أستخدم الأرقام التي واجهتها. هذه المرة تم عرضها بتنسيق أسي ، مما يجعل المقالة متنوعة بعض الشيء.
تكمن المشكلة الرئيسية في اختلاف المشكلة من الجزء الأول: تختلف أرقام الفاصلة العائمة في أجهزة الكمبيوتر عن الأرقام الحقيقية التي يستخدمها علماء الرياضيات. تصبح أقل دقة كلما زادت ، وكل المضاعفات كانت بالضرورة مضاعفات 512 في نطاق الأرقام الفاشلة.للمضاعفة 53 بت من الدقة ، وكانت هذه الأرقام أكبر بكثير من 2 ^ 53 ، لذلك كان التخفيض الكبير في الدقة أمرًا لا مفر منه. والآن يمكننا فهم المشكلة.
قام الاختبار بحساب نفس القيمة بطريقتين مختلفتين. ثم قام بفحص ما إذا كانت النتائج قريبة ، حيث تعني كلمة "التقارب" اختلافًا ضمن 1.0. أعطت طرق الحساب إجابات متشابهة جدًا ، لذلك في معظم الحالات تم تقريب النتائج إلى نفس القيمة بدقة مضاعفة. ومع ذلك ، من وقت لآخرتكون الإجابة الصحيحة قريبة من الانقلاب ، وتدور إحدى العمليات الحسابية في اتجاه والأخرى تدور في اتجاه آخر.
بشكل أكثر تحديدًا ، نتيجة لذلك ، تمت مقارنة الأرقام التالية:
- 4293431141623410688
- 4293431141623411200
بدون الأس ، من الملاحظ بشكل أكبر أنه تم الفصل بينهما بمقدار 512 بالضبط. كانت النتيجتان اللامتناهيتان اللتان تم إنشاؤهما بواسطة وظائف الاختبار تختلف دائمًا بأقل من 1.0 ، أي عندما كانت قيمتان مثل 429 ... 10653.5 و 429 ... 10654.3 ، تم تقريب كلاهما إلى 429 ... 10688. حدثت المشكلة عندما كانت النتائج الدقيقة اللانهائية قريبة من قيمة مثل 4293431141623410944. هذه القيمة بالضبط في منتصف المسافة بين زوجي. إذا كانت إحدى الوظائف تولد 429 ... 10943.9 ، والأخرى 429 ... 10944.1 ، فإن هذه النتائج ، مقسومة على قيمة 0.2 فقط ، تم تقريبها في اتجاهات مختلفة وانتهى بها الأمر على مسافة 512!
هذه هي طبيعة الانعطاف ، أو وظيفة الخطوة. يمكنك الحصول على نتيجتين ، قريبتين من بعضهما البعض بشكل تعسفي ، ولكنهما تقعان على جانبي انعطاف - نقاط بالضبط في المنتصف بين الاثنين - وبالتالي يتم تقريبهما في اتجاهات مختلفة. يوصى غالبًا بتغيير وضع التقريب ، لكن هذا لا يساعد - إنه يحرك فقط نقطة الانعطاف.
إنه يشبه إنجاب طفل في منتصف الليل - يمكن لانحراف بسيط أن يغير بشكل دائم تاريخ تسجيل الحدث (ربما عام أو قرن أو ألفية).
ربما كانت مذكرة الالتزام الخاصة بي مثيرة للغاية ، لكنها معصومة من الخطأ. شعرت كأنني متخصص فريد قادر على التعامل مع هذا الموقف:
الالتزام 6c2427457b0c5ebaefa5c1a6003117ca8126e7bc
المؤلف: Bruce Dawson
التاريخ: الجمعة ديسمبر 08 21:58:50 2017
إصلاح حساب إبسيلون للمقارنات الكبيرة والمزدوجة كانت
حياتي كلها تؤدي إلى إصلاح هذا الخطأ. [كانت حياتي كلها تقودني لإصلاح هذا الخطأ.]
في الواقع ، نادرًا ما أتمكن من إجراء تغيير في Chromium مع ملاحظة الالتزام التي ترتبط بشكل معقول بمشاركتين (2!) من مشاركاتي .
كان الإصلاح في هذه الحالة هو حساب الفرق بين زوجي متجاورين بحجم القيم المحسوبة. تم ذلك من خلال الوظيفة التالية التي نادرًا ما تستخدم . أكثر أو أقل من هذا القبيل:
epsilon = nextafter(expected, INFINITY) – expected;
if (epsilon < 1.0)
epsilon = 1.0;
تجد الدالة nextafter المضاعفة التالية (في هذه الحالة ، في الاتجاه اللانهائي) ، والطرح (الذي يتم بالضبط ، وهذا مناسب جدًا) ثم يجد الفرق بين الزوجي في قيمتهما. أعطت الخوارزمية المختبرة خطأ 1.0 ، لذلك يجب ألا يزيد إبسيلون عن هذه القيمة. هذا الحساب لـ epsilon يجعل من السهل جدًا التحقق مما إذا كانت القيم أقل من 1.0 بعيدًا أو الزوجي المجاور.
لم أحقق في سبب بدء الاختبار فجأة بالفشل ، لكنني أظن أن تكرار عداد الوقت أو تغيير نقطة بدء المؤقت هو الذي تسبب في زيادة الأرقام.
. QueryPerformanceCounter (QPC), <int64>::max(), 2^63-1. , . , , QPC 2 148 . , QPC, , , , , 3 . QPC 2^63-1 , .
, , QueryPerformanceCounter.
googletest

لقد انزعجت لأن فهم المشكلة يتطلب معرفة مقصورة على فئة معينة بتفاصيل النقطة العائمة ، لذا أردت إصلاح googletest . انتهت محاولتي الأولى بشكل سيء.
حاولت في الأصل إصلاح برنامج googletest عن طريق جعل EXPECT_NEAR تفشل عند إرسال إبسيلون صغير للغاية ، ومع ذلك ، يبدو أن الكثير من الاختبارات داخل Google ، وربما أكثر من ذلك خارج Google ، تستخدم EXPECT_NEAR بشكل غير صحيح على قيم مزدوجة. يجتازون قيمة إبسيلون أصغر من أن تكون مفيدة ، لكن الأرقام التي يقارنونها هي نفسها ، لذلك ينجح الاختبار. لقد أصلحت عشرات النقاط من استخدام EXPECT_NEAR دون الاقتراب من حل المشكلة ، لذلك استسلمت.
لم يكن الأمر كذلك حتى كنت أكتب هذا المنشور (بعد ثلاث سنوات تقريبًا من ظهور الخطأ!) أدركت مدى الأمان والسهولة في إصلاح googletest. إذا كانت الشفرة تستخدم EXPECT_NEAR مع القليل جدًا من إبسيلون ونجح الاختبار (أي أن القيم متساوية بالفعل) ، فهذه ليست مشكلة. تصبح هذه مشكلة فقط عندما يفشل الاختبار ، لذلك كان علي فقط البحث عن قيم إبسيلون صغيرة جدًا فقط في حالة الفشل وعرض رسالة إعلامية في نفس الوقت.
لقد أجريت هذا التغيير والآن تبدو رسالة الخطأ لعطل 2017 على النحو التالي:
expected_microseconds converted_microseconds 512,
expected_microseconds 4.2934311416234112e+18,
converted_microseconds evaluates to 4.2934311416234107e+18.
abs_error 1.0, double , 512; EXPECT_NEAR EXPECT_EQUAL. EXPECT_DOUBLE_EQ.
لاحظ أن EXPECT_DOUBLE_EQ لا تتحقق فعليًا من المساواة ، فهي تتحقق مما إذا كانت المضاعفات تساوي أربع وحدات في الرقم الأخير (الوحدات في المكان الأخير ، ULP). يمكنك قراءة المزيد حول هذا المفهوم في رسالتي مقارنة أرقام الفاصلة العائمة .
آمل أن يرى معظم مطوري البرامج رسالة الخطأ الجديدة هذه وأن يسلكوا المسار الصحيح ، وأعتقد أن إصلاح googletest أهم في النهاية من إصلاح اختبار Chromium.
الجزء 3: عندما x + y = x (y! = 0)
هذا نوع آخر من مشاكل الدقة عند الاقتراب من الحدود: ربما أجد نفس خطأ النقطة العائمة مرارًا وتكرارًا؟
في هذا الجزء ، سأصف أيضًا تقنيات تصحيح الأخطاء التي يمكنك تطبيقها إذا كنت تريد التحقق من شفرة مصدر Chromium أو التحقق من سبب التعطل.

عندما صادفت هذه المشكلة ، قمت بنشر تقرير خطأ بعنوان " Crash with OOM (Out of Memory) error in chrome: // tracing when zoom in "؛ هذا ليس مثل خطأ النقطة العائمة.
كالعادة ، لم أكن أبحث عن مشاكل بنفسي ، لكنني فقط أدرس chrome: // tracing ، محاولًا فهم بعض الأحداث ؛ ظهرت علامة تبويب حزينة فجأة - كان هناك فشل.
يمكنك عرض أحدث أعطال Chrome وتنزيلها على chrome: // Cracks ، لكنني أردت تحميل ملف تفريغ الأعطال في مصحح الأخطاء ، لذلك نظرت إلى مكان تخزينها محليًا:
٪ localappdata٪ \ Google \ Chrome \ بيانات المستخدم \ Crashpad \ التقارير
لقد قمت بتحميل أحدث تفريغ للأعطال إلى windbg (سيفعل Visual Studio أيضًا) ثم شرعت في التحقيق. نظرًا لتكوين خوادم رموز Chrome و Microsoft وتم تمكين خادم المصدر ، قام مصحح الأخطاء تلقائيًا بتنزيل PDB (معلومات التصحيح) وملفات المصدر المطلوبة. لاحظ أن هذا المخطط متاح للجميع - لست بحاجة إلى أن تكون أحد موظفي Google أو مطور Chromium حتى يعمل هذا السحر. يمكن العثور هنا على إرشادات حول إعداد تصحيح أخطاء Chrome / Chromium . يتطلب التنزيل التلقائي لشفرة المصدر تثبيت Python.
أظهر تحليل التعطل أن خطأ نفاد الذاكرة يحدث بسبب حقيقة أن محرك v8 (محرك JavaScript) يعمل NewFixedDoubleArrayيحاول تخصيص مصفوفة تحتوي على 75209227 عنصرًا ، والحد الأقصى المسموح به في هذا السياق هو 67108863 (0x3FFFFFF في ست عشري).
الشيء الجميل في الأخطاء التي تسببت فيها لنفسي هو أنه يمكنك محاولة إعادة إنشائها بمراقبة أكثر دقة. أظهرت التجارب أنه عند التكبير ، ظلت الذاكرة مستقرة حتى وصلت إلى النقطة الحرجة ، وبعد ذلك ارتفع استخدام الذاكرة فجأة وتحطمت علامة التبويب حتى لو لم أفعل شيئًا.
كانت المشكلة هنا أنه يمكنني بسهولة عرض مكدس المكالمات لهذا الفشل ، ولكن فقط في جزء C ++ من كود Chrome. ومع ذلك ، يبدو أن الخطأ نفسه ظهر في chrome: // tracing JavaScript code. حاولت اختباره باستخدام إصدار canary من Chrome (يوميًا) تحت مصحح الأخطاء ، وتلقيت الرسالة الغريبة التالية:
==== تتبع مكدس JS =====================================
لسوء الحظ ، لم يكن هناك أثر كومة خلف هذا الخط المثير للاهتمام. بعد التجول قليلاً في براري git ، اكتشفت أنه تمت إضافة القدرة على إخراج مكدسات مكالمات JS عبر OOM في عام 2015 ثم تمت إزالتها في ديسمبر 2019 .
لقد بحثت عن هذا الخطأ في بداية يناير 2020 (تذكر تلك الأيام الخوالي عندما كان كل شيء بريئًا وأسهل؟) ، وهذا يعني أنه تمت إزالة كود تتبع المكدس OOM من الإصدار اليومي ، لكنه ظل في تجميع ثابت ...
لذلك ، كانت خطوتي التالية هي محاولة إعادة إنشاء الخطأ في الإصدار الثابت من Chrome. أعطاني هذا النتائج التالية (قمت بتحريرها قليلاً من أجل الوضوح):
0: ExitFrame [pc: 00007FFDCD887FBD]
1: drawGrid_ [000016011D504859] [chrome: //tracing/tracing.js: ~ 4750]
2: رسم [000016011D504821] [chrome: //tracing/tracing.js: 4750]

باختصار ، كان سبب تعطل OOM هو drawGrid_ ، والذي وجدته (باستخدام صفحة البحث عن كود Chromium ) في x_axis_track.html. بعد أن قمت بتعديل هذا الملف قليلاً ، قمت بتضييقه إلى استدعاء updateMajorMarkData . تحتوي هذه الوظيفة على حلقة تستدعي الوظيفة majorMarkWorldPositions_.push ، والتي تعد السبب في المشكلة.
من الجدير بالذكر هنا أنه على الرغم من أنني قمت بتطوير متصفح ، إلا أنني ما زلت أسوأ مبرمج جافا سكريبت في العالم. المهارة في برمجة أنظمة C ++ لا تمنحني سحر "الواجهة الأمامية". كان اختراق JavaScript لفهم هذا الخطأ عملية مؤلمة للغاية بالنسبة لي.
تبدو الحلقة (التي يمكن عرضها هنا ) على النحو التالي :
for (let curX = firstMajorMark;
curX < viewRWorld;
curX += majorMarkDistanceWorld) {
this.majorMarkWorldPositions_.push(
Math.floor(MAJOR_MARK_ROUNDING_FACTOR * curX) /
MAJOR_MARK_ROUNDING_FACTOR);
}
أضفت بيانات إخراج التصحيح قبل الحلقة وحصلت على البيانات الموضحة أدناه. عندما قمت بتكبير الصورة ، بدت الأرقام التي كانت حرجة ، ولكنها ليست كافية للتسبب في حدوث عطل ، كما يلي:
firstMajorMark: 885.0999999642371
majorMarkDistanceWorld: 1e-13
ثم قمت بالتكبير لإحداث انهيار ، وحصلت على أرقام مثل هذه:
firstMajorMark: 885.0999999642371
majorMarkDistanceWorld: 5e-14
885 مقسومًا على 5e-14 هي 1.8e16 ، ودقة رقم فاصلة عائمة مزدوجة الدقة هي 2 ^ 53 ، وهي 9.0e15. لذلك ، يحدث خطأ عندما تكون mainMarkDistanceWorld (المسافة بين نقاط الشبكة) صغيرة جدًا بالنسبة إلى firstMajorMark (موقع أول علامة شبكة رئيسية) بحيث لا تؤدي إضافة حلقة ... أي ، إذا أضفنا رقمًا صغيرًا إلى رقم كبير ، فعندما يكون الصغير "صغيرًا جدًا" ، يمكن أن يظل الرقم الكبير (في التقريب القياسي / العاقل إلى أقرب وضع) مساويًا لنفس القيمة.
لهذا السبب ، تعمل الحلقة إلى أجل غير مسمى ، ويتم تنفيذ أمر الدفع حتى تقتصر المصفوفة على حجمها. إذا لم تكن هناك حدود للحجم ، فسيستمر تشغيل أمر الدفع حتى نفاد ذاكرة الجهاز بالكامل. حسنًا ، حل المشكلة؟
تبين أن الإصلاح بسيط للغاية - لا تعرض ملصقات الشبكة إذا لم نستطع:
if (firstMajorMark / majorMarkDistanceWorld > 1e15) return;

كما هو الحال غالبًا مع التغييرات التي أجريها ، يتكون إصلاح الخطأ الخاص بي من سطر واحد من التعليمات البرمجية وتعليق من ستة أسطر. أنا مندهش فقط من عدم وجود ملاحظات خماسية التعميم من خمسين سطرًا ، وتدوينًا ، ومدونة. انتظر لحظة ...
لسوء الحظ ، لا تزال إطارات مكدس JavaScript غير معروضة عند تعطل OOM ، لأن الأمر يتطلب ذاكرة لكتابة مكدسات المكالمات ، مما يعني أنها ليست آمنة في هذه المرحلة. لا أفهم تمامًا كيف سأحقق في هذا الخطأ اليوم ، عندما تمت إزالة إطارات مكدس OOM تمامًا ، لكنني متأكد من أنني سأجد طريقة.
لذلك ، إذا كنت مطورًا لجافا سكريبت تحاول استخدام أرقام كبيرة جدًا ، أو كاتب اختبار يحاول استخدام أكبر قيمة عدد صحيح ، أو تنفيذ واجهة مستخدم بتكبير غير محدود ، فمن المهم أن تتذكر أنه عندما تقترب من حدود الرياضيات العائمة ، يمكن اختراق هذه الحدود.
إعلان
خوادم التنمية هي ملحمة من Vdsina.
نحن نستخدم محركات أقراص NVMe سريعة للغاية من Intel ولا نوفر على الأجهزة - فقط المعدات ذات العلامات التجارية وأحدث الحلول في السوق!
