سؤال

سمعت أنه يمكنك إزاحة رقم إلى اليمين بمقدار 0.5 بدلاً من استخدام Math.floor().قررت التحقق من حدوده للتأكد من أنه بديل مناسب، لذلك قمت بالتحقق من القيم التالية وحصلت على النتائج التالية في جوجل كروم:


2.5 >> .5 == 2;
2.9999 >> .5 == 2;
2.999999999999999 >> .5 == 2;  // 15 9s
2.9999999999999999 >> .5 == 3;  // 16 9s

بعد بعض العبث، اكتشفت أن أعلى قيمة ممكنة لاثنين والتي، عند إزاحتها لليمين بمقدار .5، ستنتج 2 هي 2.999999999999997779553950749686919152736663818359374999999¯ (مع تكرار 9) في Chrome وFirefox.الرقم هو 2.999999999999997779¯ في IE.

سؤالي هو:ما أهمية الرقم .0000000000000007779553950749686919152736663818359374؟إنه رقم غريب جدًا وقد أثار فضولي حقًا.

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

للعلم، يتغير تسلسل الأرقام الغريب بـ 2^x.أعلى القيم الممكنة للأرقام التالية التي لا تزال مقطوعة بشكل صحيح:

for 0: 0.9999999999999999444888487687421729788184165954589843749¯
for 1: 1.9999999999999999888977697537484345957636833190917968749¯
for 2-3: x+.99999999999999977795539507496869191527366638183593749¯
for 4-7: x+.9999999999999995559107901499373838305473327636718749¯
for 8-15: x+.999999999999999111821580299874767661094665527343749¯
...and so forth
هل كانت مفيدة؟

المحلول

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

2.999999 >> 0.5

ويصبح:

Math.floor(2.999999) >> Math.floor(0.5)

والذي بدوره هو:

2 >> 0

والتحول من قبل 0 بت يعني "لا تفعل التحول"، وبالتالي ينتهي بك الأمر مع المعامل الأول، اقتطاع ببساطة إلى عدد صحيح.

وشفرة المصدر سبايدر مونكي له:

switch (op) {
  case JSOP_LSH:
  case JSOP_RSH:
    if (!js_DoubleToECMAInt32(cx, d, &i)) // Same as Math.floor()
        return JS_FALSE;
    if (!js_DoubleToECMAInt32(cx, d2, &j)) // Same as Math.floor()
        return JS_FALSE;
    j &= 31;
    d = (op == JSOP_LSH) ? i << j : i >> j;
    break;

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

alert(2.999999999999999);

وستحصل على +2.999999999999999. الآن حاول إضافة واحد أكثر 9:

alert(2.9999999999999999);

وستحصل على 3.

نصائح أخرى

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

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


تأكيد بعض الشكوك التي راودتني عند قراءة السؤال:

  • تحريك أي رقم كسري إلى اليمين x بأي رقم كسري y سيتم اقتطاعها ببساطة x, ، مما يعطي نفس النتيجة Math.floor() بينما يربك القارئ تمامًا.
  • 2.99999999999999977955395074968691915...هو ببساطة أكبر رقم يمكن تمييزه عن الرقم "3".حاول تقييمه بمفرده - إذا أضفت إليه أي شيء، فسيتم تقييمه إلى 3.يعد هذا أحد عناصر تنفيذ الفاصلة العائمة للمتصفح والنظام المحلي.

إذا كنت تريد أن تذهب أعمق، وقراءة "ما كل عالم الحاسوب يجب أن تعرفه عن العائمة نقطة الحساب": <لأ href = "http://docs.sun.com/source/806-3568/ncg_goldberg.html" يختلط = "نوفولو noreferrer"> http://docs.sun.com/source/806-3568/ncg_goldberg.html

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

في كروم:

var x = 2.999999999999999777955395074968691915273666381835937499999;
var y = 2.9999999999999997779553950749686919152736663818359375;

document.write("x=" + x);
document.write(" y=" + y);

ويطبع من: س = ص = 2.9999999999999996 3

وهذه محاولة جافا سكريبت الخروج:   تنبيه (parseFloat ( "2،9999999999999997779553950749686919152736663818359374999999"))؛

وثم حاول هذا:   تنبيه (parseFloat ( "2،9999999999999997779553950749686919152736663818359375"))؛

وماذا كنت ترى بسيطة نقطة عائمة دقة. لمزيد من المعلومات حول ذلك، ترى هذا على سبيل المثال: http://en.wikipedia.org/wiki / Floating_point # Accuracy_problems .

والمسألة الأساسية هي أن أقرب ما يمكن الحصول على قيمة نقطة عائمة لتمثيل الرقم الثاني هو أكبر من أو يساوي 3، في حين أن يغلق أن تعويم يمكن الحصول على الرقم الأول أقل بدقة من ثلاث سنوات.

وأما لماذا تحول الحق بنسبة 0.5 يفعل أي شيء عاقل على الإطلاق، يبدو أن 0.5 هو مجرد نفسه الحصول على تحويلها إلى int (0) مسبقا. ثم تعويم الأصلي (2.999 ...) هو الحصول على تحويلها إلى int قبل اقتطاع، كالعادة.

والمشغل الصحيح التحول يعمل فقط على الأعداد الصحيحة (كلا الجانبين). وهكذا، تحول الحق 0.5 بت يجب أن يكون معادلا بالضبط لتحويل الحق 0 بت. ويتم تحويل الجانب الأيسر إلى عدد صحيح قبل عملية التحول، الذي يفعل الشيء نفسه كما Math.floor ().

<اقتباس فقرة>   

وأظن أن تحويل 2،9999999999999997779553950749686919152736663818359374999999   لانها ثنائي التمثيل سيكون المنير. من المحتمل أن يكون مختلفا بعض الشيء فقط 1   من يصدق 3.

وتخمين جيد، ولكن لا السيجار. كما مزدوج عدد الدقة FP ديه 53 بت، وعدد FP الماضي قبل 3 هو في الواقع (الدقة): 2،999999999999999555910790149937383830547332763671875

ولكن لماذا هو 2،9999999999999997779553950749686919152736663818359375

(وهذا هو بالضبط، وليس 49999 ...!)

والذي هو أعلى من وحدة للعرض الماضية؟ التقريب. روتين تحويل (سلسلة لعدد) ببساطة هو مبرمجة بشكل صحيح لجولة وإدخال المقبل عدد النقطة العائمة.

و2،999999999999999555910790149937383830547332763671875

و....... (القيم بين، وزيادة) -> جولة أسفل

و2،9999999999999997779553950749686919152736663818359375

و....... (القيم بين، وزيادة) -> جولة يصل إلى 3

3

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

والآن

3 = 11. (ثنائي)

و2.999 ... = +10.11111111111 ...... (ثنائي)

ويتم تعيين كافة البتات، والرقم هو الغريب دائما. وهذا يعني أن نصف عدد المحدد سيتم القبض عليهم، لذلك كنت تحصل على غريبة ..... 49999 الفترة لأنه يجب أن يكون أصغر من نصف المحدد لتكون مميزة من 3.

وأظن أن تحويل 2،9999999999999997779553950749686919152736663818359374999999 لتمثيلها ثنائي سيكون المنير. من المحتمل أن يكون مختلفا بعض الشيء فقط 1 من 3 صحيح.

وإضافة إلى الإجابة جون، واحتمالات هذا يجري أكثر performant للمن Math.floor صغيرة بزوال.

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

وتجدر الإشارة إلى أن عدد ".0000000000000007779553950749686919152736663818359374" تماما وربما ابسيلون ، يعرف بأنه "أصغر رقم E مثل أن (1 + E)> 1."

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