إلى مسألة التحولات وغيرها من العمليات

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


من خلال الاطلاع على الكود الموجود على أحد المنتديات المخصصة لـ Arduino ، وجدت طريقة ممتعة للعمل برقم الفاصلة العائمة (PT). الاسم الشائع الثاني للأرقام في هذا التنسيق هو النقطة العائمة ، ولكن الاختصار (PP) الذي ينشأ في هذه الحالة شخصيًا يسبب ارتباطات مختلفة تمامًا بالنسبة لي ، لذلك سنستخدم هذا الخيار. الانطباع الأول (من الكود المرئي) هو نوع القمامة المكتوب هنا (يجب أن أقول أن الثاني هو نفسه ، على الرغم من أن هناك فروق دقيقة ، ولكن المزيد عن ذلك لاحقًا) ، ولكن السؤال الذي يطرح نفسه - كيف هو ضروري حقًا - الإجابة التي يتم تقديمها في مزيد من النص.

الجزء الأول - استجواب


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

  1. استخدام التنسيق٪ f يستلزم توصيل المكتبة للعمل بنقطة عائمة وإصدار موسع من دالة prntf (أو بالأحرى ، يجعل من المستحيل استخدام نسختها المقطوعة) ، مما يؤدي إلى زيادة كبيرة في حجم الوحدة القابلة للتنفيذ ،
  2. يتطلب الحل القياسي وقتًا طويلاً (يعمل دائمًا برقم مزدوج الدقة) ، وهو ما قد يكون غير مقبول في هذه الحالة بالذات ،
  3. حسنًا (أخيرًا وليس آخرًا) ، إنه أمر مثير للاهتمام.


للبدء ، فكر في الخيار الذي تم اقتراحه في المادة أعلاه ، مثل:

for (float Power10=10000.0; Power10>0.1; Power10/=10.0; ) {char c=(int)(Fdata/Power10); Fdata -=Power10*c; }; 

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

الجزء الثاني - تقييم أداء العمليات الأولية


أول عملية مثيرة للاهتمام هي إضافة (الطرح ، بمعنى الوقت الذي تقضيه ، فهي معادلة) لعدد صحيح من الأرقام ، ويمكننا أن نفترض أن الأمر يستغرق وحدة من الوقت (دورة الساعة) مع التحذير التالي - وهذا صحيح فقط للبيانات "الأصلية". على سبيل المثال ، بالنسبة لسلسلة MK AV AVR ، هي كلمة مكونة من 8 بتات ، أما بالنسبة لـ MSP430 فهي كلمة مكونة من 16 بتًا (وبالطبع أصغر في الحجم) ، أما بالنسبة إلى Cortex-M فهي كلمة مكونة من 32 بتًا وما إلى ذلك. ثم يمكن تقدير عملية إضافة أرقام بطول H مرات أكثر من الرقم الأصلي على أنها دورات H. هناك استثناءات ، على سبيل المثال ، AddW في وحدات تحكم AVR ، لكنها لا تلغي القاعدة.

العملية التالية هي ضرب الأعداد الصحيحة (ولكن ليس القسمة ، فهي تختلف من حيث السرعة) وبالنسبة له ليس كل شيء بهذه البساطة. بادئ ذي بدء ، يمكن تنفيذ الضرب في الأجهزة ، على سبيل المثال ، في AVR MEGA يتطلب دورتين على مدار الساعة ، وفي 51 محسنة ما يصل إلى 6 (لضرب الأرقام الأصلية).

لكن ضع في اعتبارك الحالة عندما لا يكون هناك تطبيق للأجهزة وعلينا تنفيذ الضرب في شكل روتين فرعي. حيث أنه عند ضرب أرقام بت H ، يتم الحصول على منتج بتة 2H ، ثم يمكن العثور على تقدير الإصدار الكلاسيكي مع التحولات على النحو التالي: نحتاج إلى تحولات H للعامل بدورة ساعة واحدة لكل نوبة ، وتحولات H للعامل الثاني 2H طويلًا مع دورتين على مدار الساعة لكل نوبة ، ثم يتخذ H ، في المتوسط ​​، إضافات N / 2 من الأرقام بطول 2H ، في الختام ، تنظيم دورة من 2 التدابير. إجمالي علامات التجزئة + 2 + + 2 / 2 + 2 = 7 ، والقيام بعمليات حسابية منها لا يتطلب سوى علامات N (كفاءة نجاح باهر ، على الرغم من أننا تمكنا من الالتفاف على المحرك).

وهذا يعني ، لمضاعفة رقمين 8p في 8p MK ، يلزم 56 دورة ، ولمضاعفة 16p هناك بالفعل 112 دورة (أقل قليلاً ، لكننا نهمل القيمة الدقيقة) دورات ، وهو أكثر إلى حد ما كنا نريد. لحسن الحظ ، يمكن تعديل اتجاه التحولات وهناك طريقة فريدة من الضرب ، والتي سوف تتطلب فقط تحولات H من عدد من 2H أرقام وإضافات H / 2 من الأرقام الأصلية ، مما يحسن وقت خوارزمية الضرب إلى 0 + 2 + 1 + 1/2 + 2 = 5.5 - بالطبع ، لا يمكن مقارنتها بتنفيذ الأجهزة ، ولكن على الأقل بعض المكاسب دون فقدان الوظيفة. هناك تحسينات على هذه الخوارزمية ، على سبيل المثال ، تحليل 2 بت لكل دورة ، لكنها لا تغير الموقف بشكل كبير - وقت الضرب بأوامر من الحجم يتجاوز وقت الإضافة.

ولكن في حالة القسمة ، يكون الموقف أسوأ - فحتى القسم الذي يتم تنفيذه للأجهزة يفقد ضعف الضرب ، ويوجد عدد من أعضاء الكنيست مع تكاثر الأجهزة ، ولكن بدون تقسيم للأجهزة. في ظل ظروف معينة ، يمكن الاستعاضة عن القسمة بضرب بالمقلوب ، ولكن هذه الشروط محددة وتعطي نتيجة مماثلة - يلزم تكرار اثنين من الضرب متبوعًا بالمجموع ، لذلك هناك خسارة مضاعفة. إذا قمنا بتطبيق القسمة على أنها برنامج فرعي ، عندئذ تقوم N بالتحويل من المقسوم 2H الطويل ، والطرح H من المقسوم 2H الطويل ، H التحولات للنتيجة ، التنظيم 2H للدورة مطلوب ، ولكن كل هذا يسبقه محاذاة ، والتي ستستغرق دورة 5H أخرى ، وبالتالي فإن الرقم الكلي هو 2 + 2 + 1 + 2 + 5 = 12 ، والذي هو حوالي 2 مرات أسوأ من الضرب.

حسنًا ، سننظر الآن في عمليات PT ، وهنا يكون الموقف متناقضًا إلى حد ما - تتطلب عملية الضرب وقتًا تقريبًا بقدر الأعداد الصحيحة للأعداد الصحيحة (المقابلة لقدرة البتات ، كقاعدة ، 24 بت) ، نظرًا لأننا يجب أن نضاعف المانتيسا ونضيف فقط الطلبات ، فإن التطبيع مطلوب مع تقسيم جيد أيضا ، وتقسيم السرعوف وطرح أوامر ، مرة أخرى ليست هناك حاجة للتطبيع. لذلك ، بالنسبة لهاتين العمليتين ، فإن الخسارة مقارنة بالأعداد الصحيحة ليست كبيرة للغاية ، على الرغم من أنها تحتوي على مكان.

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

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

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

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

السؤال الأول هو لماذا يتم تخزين السرعوف في الكود المباشر وليس في الكود الإضافي؟ إجابتي هي أنه من الأسهل التعامل مع سوسة عادية مع بت مخفي (اختياري).

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

السؤال الثالث هو لماذا يتم ترميز الإشارة السلبية بوحدة ، وليس صفر ، لأنه عندئذ سيكون من الممكن ببساطة مقارنة النقطتين كأعداد صحيحة؟ جوابي هو أنني لا أعرف ، إنه مجرد "أمر معتاد هنا".

الجزء الثالث - التفسيرات المطلوبة


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

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

وفقًا لافتراض أن مجموعة البتات لها طول n (نعددها على التوالي من 0 إلى n-1) ويتم وزنها بشكل موحد بخطوة 2 ويكون للبت الأقل أهمية (بالرقم 0) وزن 1 (والذي ، عمومًا ، ليس ضروريًا على الإطلاق ، نحن فقط لقد اعتدنا على مثل هذه الأشياء ، ويبدو أنها واضحة بالنسبة لنا) نحصل على تمثيل ثنائي للرقم ، حيث تبدو صيغة الخفض كما يلي: الرقم المعروض بواسطة مجموعة البتات (2) = (0)*2^0 + (1)*2^1 + ... + (-1)*2^(-1) أو في شكل 2() = (0)+2*((1)+2*(...+2*((-1))..))) ، فيما يلي ، يشير B (k) قليلاً بالرقم k. لاحظ أنه ضمن وجهة نظر مختلفة لا تفرض أي قيود على موقع بايتات الأرقام في الذاكرة ، ولكن سيكون من المنطقي وضع البايتات المنخفضة في العناوين السفلية (هذه هي الطريقة التي قمت بها بسهولة وبطريقة طبيعية بحل "الحجة الأبدية للسلاف فيما بينهم" فيما يتعلق بأي غاية أكثر ملاءمة لكسر البويضة).

مع هذا التفسير لمجموعة من البتات بطول n (= 8) ، نحصل على تمثيل للأرقام من 0 إلى (2 ^ n) -1 (= 255) (فيما يلي بين قوسين ستكون هناك قيمة محددة لمجموعة من 8 بت) ، والتي تحتوي على عدد من اللافت للنظر وخصائص مفيدة ، وهذا هو السبب في أنه أصبح واسع الانتشار. لسوء الحظ ، فإنه يحتوي أيضًا على عدد من العيوب ، أحدها أنه لا يمكننا تمثيل الأرقام السالبة في مثل هذا السجل من حيث المبدأ.

يمكنك تقديم مجموعة متنوعة من الحلول لهذه المشكلة (تمثيل الأرقام السالبة) ، والتي من بينها أيضًا أهمية عملية ، وهي مدرجة أدناه.

يوصف التمثيل بإزاحة بالمعادلة H = N2 (n) - الإزاحة (C) ، حيث N2 هو الرقم الذي تم الحصول عليه بترميز ثنائي مع n bits ، و C هي بعض القيمة المحددة مسبقًا. ثم نمثل الأرقام من 0-C إلى 2 ^ (n) -1-C ، وإذا اخترنا C = 2 ^ (n-1) -1 (= 127) (هذا اختياري تمامًا ولكنه مناسب جدًا) ، ثم نحصل على المدى من 0- (2 ^ (n-1) -1) (= - 127) إلى 2 ^ (n-1) (= 128). الميزة الرئيسية لهذا التمثيل هي الرتابة (علاوة على ذلك ، الزيادة) خلال الفترة الزمنية بأكملها ، وهناك أيضًا عيوب ، نبرز من بينها عدم التناسق (هناك عمليات أخرى تتعلق بتعقيد إجراء العمليات على الرقم في هذا التمثيل) ، ولكن مطوري معيار IEEE 457 (هذا هو المعيار ل تحول PT) هذا الخلل إلى فضيلة (باستخدام قيمة إضافية لتشفير الموقف نان) ، والذي يؤكد مرة أخرى على الإخلاص للقول بارد: "إذا كنت أعلى من الخصم ، فهذه هي مصلحتك. إذا كان الخصم أطول منك ، فهذه أيضًا مصلحتك. "

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

التمثيل في شكل رمز مباشر عندما تمثل إحدى البتات (الأهم) الإشارة المشفرة للرقم H = (-1) ^ B (n-1) * P2 (n-1) يتراوح من 0- (2 ^ (n-1)) -1) (= -127) إلى 2 ^ (n-1) -1 (= 127). من المثير للاهتمام أن أشير إلى أنني أعلنت للتو استحالة التماثل الأساسية ، وهنا بوضوح - الحد الأقصى لعدد موجب يمكن تمثيله يساوي معامل الحد الأدنى للرقم السالب القابل للتمثيل. يتم تحقيق هذه النتيجة من خلال وجود تمثيلين للصفر (00 ... 00 و 10 ... 00) ، والذي يعتبر عادةً العيب الرئيسي لهذه الطريقة. هذا في الحقيقة عيب ، ولكنه ليس فظيعًا كما هو معتاد ، حيث توجد عوامل أكثر أهمية تحد من استخدامه.

تمثيل الرمز العكسي ، عندما نقلب في التمثيل المباشر جميع وحدات بت قيمة الأرقام السالبة H = (1-B (n-1)) * P2 (n-1) + B (n-1) * (2 ^ (n -1) -CH2 (n-1)) - هذا من التعريف ، يمكنك عمل صيغة أكثر قابلية للفهم H = Ch2 (n-1) -B (n-1) * (2 ^ (n-1) -1) ، والذي يسمح لنا بتمثيل الأرقام من 0-2 ^ (n-1) +1 (= - 127) إلى 2 ^ (n-1) -1 (= 127). يمكن ملاحظة أن هذا التمثيل قد تم تهجيره ، لكن التشرد يتغير تدريجياً ، مما يجعل هذا التمثيل غير رتيب. مرة أخرى ، لدينا اثنين من الأصفار ، وهي ليست مخيفة للغاية ، وحدوث نقل دائري أثناء الجمع هو أسوأ بكثير ، مما يخلق مشاكل معينة في تنفيذ ALU.

للقضاء على العيب الأخير للتمثيل السابق بسيط للغاية ، يكفي تغيير الإزاحة بواحد ، ثم نحصل على = 22 (n-1) -B (n-1) * 2 ^ (n-1) ويمكننا تمثيل الأرقام من 0-2 ^ ( n-1) (= - 128) إلى 2 ^ (n-1) -1 (= 127). من السهل أن نرى أن التمثيل غير متماثل ، لكن الصفر فريد من نوعه. الأكثر إثارة للاهتمام هو الخاصية التالية ، "من الواضح تماما أن" نقل الحلقة لا يحدث لعملية نوع الإضافة ، وهذا هو السبب (جنبا إلى جنب مع غيرها من الميزات الممتعة) للتوزيع العالمي لهذه الطريقة المعينة لتشفير الأرقام السالبة.

دعنا نضع جدولًا من القيم المثيرة للاهتمام لطرق مختلفة من أرقام الترميز ، مع الإشارة إلى H بالقيمة 2 ^ (n-1) (128)
بت00..0011/0110..0011.11
ح (ن)0H-1 (127)ح (128)2 * ح -1 (255)
ح (ن -1)0H-1 (127)0H-1 (127)
إزاحة ن-H + 1 (-127)01ح (128)
مباشرة0H-1 (127)0-H + 1 (-127)
عكس0H-1 (127)-H + 1 (-127)0
إضافة0H-1 (127)- ح (-128)-1

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

الجزء الرابع - في الواقع حل المشكلة الأصلية (متأخرا أفضل من ألا تأتي أبدا).

استطراد صغير


بادئ ذي بدء ، أردت أن أطبع PT بتنسيق سداسي عشري (وفي النهاية قمت بذلك) ، لكن بشكل غير متوقع / غير متوقع تمامًا (كنت بحاجة إلى استبدال) صادفت النتيجة التالية. ما رأيك سوف تتم طباعته نتيجة لتنفيذ العوامل:

 printf("%f %x", 1.0,1.0); printf("%f %x",2.0,2.0); printf("%x %d",1.0,1.0); printf("%x %d",2.0,2.0); 

، يرجى الانتباه أيضًا إلى البناء التالي ونتيجته:

 printf("%x %x %f",1.0,1.0); 

لن أقدم تفسيرات لهذه الظاهرة "ذكية بما فيه الكفاية".

ومع ذلك ، كيف يمكننا طباعة التمثيل السداسي العشري لـ PT بشكل صحيح؟ الحل الأول واضح - الاتحاد ، ولكن الثاني هو لعشاق سطر واحد printf ("٪ x" ، * ((int *) (& f))) ؛ (أعتذر إذا تعرض شخص ما للإهانة من بين قوسين إضافيين ، لكنني لم أستطع أبدًا ، ولم أكن أقصد أبدًا ، تذكر أولويات العمليات ، لا سيما بالنظر إلى أن الأقواس لا تنشئ رمزًا ، لذا سأستمر في فعل الشيء نفسه). وهنا هو ، حل المهمة - نرى سلسلة من الأحرف ، 0x45678 ، والتي تحدد الرقم المطلوب بشكل فريد بالنسبة لنا ، ولكن بطريقة لا يمكننا (لا أعرفها عنك ، بالتأكيد) أن نقول أي شيء واضح عن هذا الرقم. أعتقد أن الأكاديمي كارنال ، الذي كان يمكن أن يشير إلى وجود خطأ في الشريط المثقوب مع الكود المصدري ، كان سيتعامل مع هذه المهمة ، لكن ليس الجميع متقدمًا ، لذلك سنواصل.

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

للقيام بذلك ، نعود إلى تنسيق PT (فيما يلي ، أعتبر فقط تعويمًا) ، وهو عبارة عن مجموعة من البتات التي يمكنك من خلالها استخراج (بموجب قواعد معينة) ثلاث مجموعات من البتات لتمثيل ثلاثة أرقام - علامة (علامات) ، مانتيسا (م) وترتيبها (p) ، وسيتم تحديد الرقم المطلوب المشفر بواسطة هذه الأرقام بالصيغة التالية: Cs * Chm * Chn. هنا ، الرموز تحدد الأرقام الممثلة بمجموعة البتات المقابلة ، وبالتالي ، من أجل العثور على الرقم المطلوب ، نحتاج إلى معرفة القوانين التي نستخرج بها هذه المجموعات الثلاث من المجموعة الأصلية من البتات ، وكذلك نوع التشفير لكل منها.

في حل هذه المشكلة ، ننتقل إلى معيار IEEE ونكتشف أن العلامة هي واحدة (أقدم) بت من المجموعة الأصلية وصيغة تشفير Cs = (- 1) ^ B (0). يشغل الترتيب الـ 8 بتات التالية العالية ، وهو مكتوب في الكود مع إزاحة 127 ، ويمثل قوة اثنين ، ثم Cn = 2 ^ (C2 (8) -127). يأخذ Mantissa الترتيب التالي المكون من 23 رقمًا ويمثل الرقم Chm = 1 + Ch2 (23) / 2 ^ 23.

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

لسوء الحظ ، نحن فقط في بداية الرحلة ، نظرًا لأن القليل من قراء هذا المنشور في السجل "+ 1.625 * 2 ^ 3" يتعرفون على رقم الحظ ، والذي تم ترميزه بواسطة العلامة العشرية الأكثر شيوعًا "13" ، وتخمين في السجل "1.953125 * 2 ^ 9 "بسيط" 1E3 "أو" 1 * 10 ^ 3 "أو" 1000 "المألوف للغاية قادرون على وحدات من الناس بشكل عام ، أنا بالتأكيد لا أنتمي لهم. من الغريب كيف حدث ذلك ، لأننا أكملنا المهمة الأولية ، والتي توضح مرة أخرى مدى حرصك على معالجة المستحضرات. وهذه النقطة ليست أن الترميز العشري أفضل أو أسوأ من المعيار الثنائي (في هذه الحالة ، الشيطان مبني على الدرجة) ، لكننا معتادون على العشرية لأن الطفولة وإعادة تكوين الناس أصعب بكثير من البرنامج ، لذلك سنقدم لنا الدخول إلى أكثر دراية.

من وجهة نظر الرياضيات ، لدينا عملية بسيطة - هناك سجل PT = (- 1) ^ s * m * 2 ^ n ، ونحن بحاجة إلى تحويله إلى النموذج PT = (-1) s '* m' * 10 ^ n '. نحن نساوي ، نحول ونحصل على (أحد الخيارات الممكنة) حلول s '= s' ، m '= m ، n' = n * log (2). إذا تركنا الأقواس ، نحتاج إلى ضرب عدد غير منطقي بشكل صريح (يمكن القيام بذلك إذا تم ترشيد الرقم ، لكننا سنتحدث عن هذا لاحقًا) ، ثم يبدو أن المشكلة قد تم حلها حتى نرى الإجابة ، لأنه إذا كان السجل مثل "+1.953125 * 2 ^ 9 "يبدو لنا غامضا ، السجل" + 1.953125 * 10 ^ 2.70927 "أقل قبولا ، على الرغم من أنه يبدو أنه لم يكن هناك مكان أسوأ.

— 10 '= * 10^{ * lg(2)}, '= [ * lg(2)], . (1.953125*10^0.7 0927)*10^2=«10*10^2», , , .

, :

  1. () (lg(2)) ( );
  2. ( );
  3. (10) (...);
  4. (« , ...»).

, , , 1. , * lg(2), «» , =0 ( =/lg(10)). , « », « ». . , , ' = * lg(2) * [lg(2) *256 + 1/2] / 256 , 1/2/77 = 1/144, , 1/100. — , . [4.501]=5, [4.499]=4 , , 0.002/4.5=0.04%, 1/4=25%. , , . , , , , , , .

'=*77/256

, . 24 , 2^-24=2^-4*2^-20=16^-1*(2^10)^-2~(10)^-1*(10^3)^-2=10^-7, 7 . 24 ( ). , 32 ( ) , 100 (256) , .



' = * 10^{ * lg(2)}

— 1) , 2) , , , , , . — , , , .

« , , »

q(10^x) = Δ(10^x)/10^x = (10^(x +Δx) — 10^x)/10^x = 10^Δx -1 = 10^(x*qx)-1,
10^(x*qx) >~ 10^(x*0) + (10^(x*0))'*qx = 1 + x*ln(10)*10^(0)*qx = 1+x*ln(10)*qx,

q(10x)=xln(10)qx.

— , , =127, 292 , , .

, 24 32 ( , ), , (*lg(2)) 32 , , 1'292'914'005/2^32. , , (int)((lg(2)*float(2^32))+0.5), 04d104d42, , .

, , , , .
10 0 1 . , , , , , ''=lg(2)*i+(''-lg(2)*i), 2 , ( ), lg(2) 10^'' ( ).

, lg(2) , , . , , , , 10-7 9 , 1+9*2=19 32- , . '=*lg(2) , .

32- 1+19+1=21

— , — , . , — ( ) , , .

— — (2^8=256) ['] ( 10) {'} ( ), . — =*2^=*10^'*(2^/10^')=(*(2^/10^'))*10^'.

256*3 ( 24 , ) + 256*1 ( 10 2) = 1 . 24*24 ( 32*32), .

, ( , ). , , ( 256 10) . , ,

2^-/10^-' = 1/(2^/10^') != 2^/10^',

. , . , — 18 , , , , 512 . — , , , , .

- , ( ) . ( )

=*2^=*2^(0+1)=*10^'*(2^(0+1)/10^')=*(2^0/10^')*2^1*10^',

0- , 1=-0. , .

— , 0 ? , — 10 . — 32*32, 24 , 8 8 . 256/8*4=32*4=128 — 8 .

0, , 32/2=16 , , ( ) .

, adafruit

 const UINT8 Bits[] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80}; ... data = data | Bits[n]; 

, 1 << n AVR . , , .

, , , ( godbolt, , , ) , .

( , 1 )

  ldi r18,lo8(4) sbrs r25,1 ldi r18,lo8(1) sbrc r25,0 lsl r18 sbrs r25,2 swap r18 

, , 8:7 8 (, , 16 — — « , , , »). — , : « „ 12 “ (» ", , ).



= * 2^=( * [/8]) * 2^(%8) * 10^[/8],

- - . , 32*32(24*24) . 32 10 , ( , ) .

— ,

const uint32_t Data[32] PROGMEM = { 0xF82345,… }

, , , . , , , ( )

 #define POWROUD(pow) ((uint8_t)((pow & 0x07)*log(2)+0.5)) #define MULT(pow) (2^pow / 10^POWROUND(pow)) #define MULTRAW(pow) (uint32_t((MULT(pow) << 24) +0.5)) #define BYTEMASK 0xFF #define POWDATA(pow) ((POWROUND(pow) & BYTEMASK)| (MULTRAW(pow) & (~BYTEMASK))) const uint32_t Data[(BYTEMASK/8)+1] = { POWDATA(0x00),POWDATA(0x08), ..POWDATA(0xF8)} 

, , , .

, , , , . . :

1.953125*2^9=1.953125*2^(8+1)=1.953125*42949673/256/256/256(2.56)*2*10^2=10*10^2

1000. , , , , , , , .

Source: https://habr.com/ru/post/ar439578/


All Articles