Blue Caterpillar: Nun, Sie werden uns nicht unterkriegen. Wir sitzen uns, wir wissen: Sie warten auf unsere Transformation. Und was? Aber nichts! Wir sitzen, rauchen, warten ...
Alice Puppe: Was?
Blaue Raupe: Was, warum! Von Transformationen. Das Haus in Rauch, der Rauch in eine Dame und die Dame in eine Mutter. Los geht's. Nicht stören, nicht nach vorne springen, sonst verwandeln Sie sich vorzeitig in eine Art Schmetterling.
Beim Durchsehen des Codes in einem der Arduino gewidmeten Foren fand ich eine unterhaltsame Möglichkeit, mit einer Gleitkommazahl (PT) zu arbeiten. Der zweite gebrĂ€uchliche Name fĂŒr Zahlen in diesem Format ist Gleitkomma, aber die AbkĂŒrzung (PP), die in diesem Fall persönlich auftritt, verursacht fĂŒr mich völlig andere Assoziationen, daher werden wir diese Option verwenden. Der erste Eindruck (aus dem Code, den ich gesehen habe) ist, welche Art von MĂŒll hier geschrieben ist (ich muss sagen, dass der zweite derselbe ist, obwohl es Nuancen gibt, aber dazu spĂ€ter mehr), aber die Frage stellt sich - wie ist es wirklich notwendig - die Antwort, auf die in gegeben wird weiterer Text.
Erster Teil - Fragen
Wir formulieren das Problem - wir mĂŒssen eine Gleitkommazahl auf der Konsole drucken (in eine symbolische Darstellung verwandeln), ohne die Druckoptionen zu verwenden, die fĂŒr diesen Zweck vorgesehen sind. Warum wollen wir das alleine machen -
- Die Verwendung des% f-Formats erfordert das Verbinden der Bibliothek fĂŒr die Arbeit mit einem Gleitkomma und einer erweiterten Version der prntf-Funktion (oder macht es vielmehr unmöglich, die abgeschnittene Version zu verwenden), was zu einer signifikanten VergröĂerung des ausfĂŒhrbaren Moduls fĂŒhrt.
- Eine Standardlösung benötigt viel Zeit (sie arbeitet immer mit einer doppelten Genauigkeit), was in dieser speziellen Situation möglicherweise nicht akzeptabel ist.
- Nun (zu guter Letzt), es ist einfach interessant.
Betrachten Sie zunÀchst die Option, die im obigen Material vorgeschlagen wurde, etwa:
for (float Power10=10000.0; Power10>0.1; Power10/=10.0; ) {char c=(int)(Fdata/Power10); Fdata -=Power10*c; };
und wir sind uns einig, dass er das Problem vollstĂ€ndig löst. DarĂŒber hinaus ist dies keine schlechte Option, da die Geschwindigkeit durchaus akzeptabel sein kann. Schauen wir uns diesen Moment genauer an - wir sehen die Aufteilung der PT-Zahlen, aber wenn wir tiefer in das Wesentliche des Problems eintauchen, stellt sich heraus, dass es fast so schnell ist wie die Aufteilung von ganzen Zahlen der entsprechenden Bittiefe. Bevor Sie die Leistung des Algorithmus bewerten, sollten Sie die Leistung verschiedener Elementaroperationen bewerten, die wir ausfĂŒhren werden.
Teil zwei - Leistungsbewertung elementarer Operationen
Die erste interessante Operation ist die Addition (Subtraktion im Sinne der aufgewendeten Zeit, sie sind Ă€quivalent) von ganzen Zahlen, und wir können davon ausgehen, dass eine Zeiteinheit (Taktzyklus) mit der folgenden EinschrĂ€nkung benötigt wird - dies gilt nur fĂŒr "native" Daten. FĂŒr AVR der MK-Serie ist es beispielsweise ein 8-Bit-Wort, fĂŒr MSP430 ein 16-Bit-Wort (und natĂŒrlich kleiner), fĂŒr Cortex-M ein 32-Bit-Wort und so weiter. Dann kann die Operation des HinzufĂŒgens von Zahlen mit einer LĂ€nge von H-mal mehr als der nativen als H-Zyklen geschĂ€tzt werden. Es gibt Ausnahmen, z. B. AddW in AVR-Controllern, die Regel wird jedoch nicht aufgehoben.
Die nĂ€chste Operation ist die Multiplikation von ganzen Zahlen (aber nicht die Division, sie unterscheidet sich in Bezug auf die Geschwindigkeit) und fĂŒr ihn ist nicht alles so einfach. Erstens kann die Multiplikation in Hardware implementiert werden und erfordert beispielsweise in AVR MEGA 2 Taktzyklen und in den verbesserten 51 sogar 6 (zum Multiplizieren nativer Zahlen).
Betrachten Sie jedoch den Fall, in dem keine Hardware-Implementierung vorhanden ist und die Multiplikation in Form einer Unterroutine implementiert werden muss. Da beim Multiplizieren von H-Bit-Zahlen ein 2H-Bit-Produkt erhalten wird, kann die SchĂ€tzung der klassischen Version mit Verschiebungen wie folgt gefunden werden: Wir benötigen H-Verschiebungen des Faktors mit 1 Taktzyklus pro Verschiebung, H-Verschiebungen des zweiten Faktors 2H lang mit 2 Taktzyklen pro Verschiebung, dann trifft H Entscheidungen und im Durchschnitt N / 2 Additionen von Zahlen mit einer LĂ€nge von 2H, abschlieĂend die Organisation eines Zyklus von 2 MaĂnahmen. Insgesamt + 2 + + 2 / 2 + 2 = 7 Ticks, und das tatsĂ€chliche AusfĂŒhren von Rechenoperationen erfordert nur N Ticks (Wow-Effizienz, obwohl wir es geschafft haben, den Motor zu umgehen).
Das heiĂt, um zwei 8p-Zahlen mit 8p MK zu multiplizieren, sind 56 Zyklen erforderlich, und um 16p-Zahlen zu multiplizieren, gibt es bereits 112 Zyklen (etwas weniger, aber wir vernachlĂ€ssigen den genauen Wert), was etwas mehr ist, als wir wollten. GlĂŒcklicherweise kann die Richtung der Verschiebungen geĂ€ndert werden, und es gibt eine einzigartige Art der Multiplikation, die nur H-Verschiebungen der Anzahl von 2H-Ziffern und H / 2-Additionen von nativen Zahlen erfordert, was die Betriebszeit des Multiplikationsalgorithmus auf 0 + 2 + 1 + 1/2 verbessert + 2 = 5,5 - natĂŒrlich kann es nicht mit der Hardware-Implementierung verglichen werden, aber zumindest ein gewisser Gewinn ohne Funktionsverlust. Es gibt Verbesserungen an diesem Algorithmus, zum Beispiel die Analyse von 2 Bits pro Zyklus, aber sie Ă€ndern die Situation nicht drastisch - die Zeit der Multiplikation mit GröĂenordnungen ĂŒberschreitet die Zeit der Addition.
Bei der Division ist die Situation jedoch schlimmer - selbst die durch Hardware implementierte Division verliert fast doppelt so viel durch Multiplikation, und es gibt MKs mit Hardware-Multiplikation, jedoch ohne Hardware-Division. Unter bestimmten Bedingungen kann die Division durch Multiplikation mit dem Kehrwert ersetzt werden. Diese Bedingungen sind jedoch spezifisch und ergeben ein Àhnliches Ergebnis. Es sind zwei Multiplikationsiterationen erforderlich, gefolgt von der Summe, sodass ein zweifacher Verlust entsteht. Wenn wir die Division als Unterprogramm implementieren, sind H-Verschiebungen des Divisors 2H lang, H-Subtraktionen von der teilbaren 2H-LÀnge, H-Verschiebungen des Ergebnisses, 2H-Organisation des Zyklus erforderlich, aber all dies geht eine Ausrichtung voraus, die weitere 5H-Zyklen benötigt, sodass die Gesamtzahl 2 betrÀgt + 2 + 1 + 2 + 5 = 12, was ungefÀhr 2 mal schlechter ist als die Multiplikation.
Nun, schauen wir uns die PT-Operationen an, und hier ist die Situation etwas paradox - die Multiplikationsoperation benötigt fast so viel Zeit wie fĂŒr ganze Zahlen (entsprechend der BitkapazitĂ€t, in der Regel 24 Bit), da wir die Mantisse multiplizieren und nur die Ordnungen addieren mĂŒssen, die Normalisierung nicht erforderlich. Wenn die Division auch gut ist, teilen Sie die Mantisse und subtrahieren Sie die Ordnungen, eine Normalisierung ist wiederum nicht erforderlich. Daher ist fĂŒr diese beiden Operationen der Verlust im Vergleich zu ganzen Zahlen nicht zu signifikant, obwohl er einen Platz hat.
Die Operation der Addition und Subtraktion erfordert jedoch zunĂ€chst die Ausrichtung der Ordnungen (und dies sind Verschiebungen und es kann viele geben, obwohl es Nuancen gibt), dann die Operation selbst und (beim Subtrahieren) die Normalisierung (auch beim Addieren, aber es dauert nicht mehr als eine Verschiebung ), was zeitaufwĂ€ndig ist, daher sind die Operationen dieser Klasse fĂŒr PT viel langsamer als fĂŒr ganze Zahlen, insbesondere relativ gesehen.
Kehren wir zu unseren Schafen zurĂŒck und stimmen zu, dass die vorgeschlagene Methode auf der Grundlage der vorherigen SchĂ€tzungen möglicherweise nicht zu lang ist, zumal sie sofort das Ergebnis liefert, aber eine erhebliche EinschrĂ€nkung aufweist - sie ist auf einen sehr begrenzten Bereich von PT-Eingabewerten anwendbar. Daher wird nach einer universellen (mehr oder weniger) Lösung gesucht.
Machen Sie sofort einen Vorbehalt, dass unsere Lösung im Allgemeinen keine Gleitkommaoperationen (vom Wort her) verwenden sollte, um die VorzĂŒge unserer Option hervorzuheben. Und auf die verblĂŒffte Frage, wie dann eine Nummer dieses Typs angezeigt wird, wenn die Operationen nicht verfĂŒgbar sind, antworten wir - dies kann beispielsweise beim Lesen von Informationen von einem Lichtsensor (wie im ursprĂŒnglichen Beispiel) auftreten, der Daten im PT-Format erzeugt.
Wie genau die Anzahl der PTs angeordnet ist, können Sie leicht auf zahlreichen Websites finden, es gab einen kĂŒrzlich erschienenen Artikel ĂŒber HabrĂ©, es sollte keine Probleme damit geben. Dennoch sind eine Reihe von Fragen fĂŒr das PT-Format im Stil âWenn ich der Regisseur wĂ€reâ von Interesse - warum dies so ist und nicht anders. Ich werde einigen von ihnen meine Antworten geben. Wenn jemand mehr richtig weiĂ, bitte kommentieren.
Die erste Frage ist, warum die Mantisse im direkten Code und nicht im zusÀtzlichen Code gespeichert ist. Meine Antwort ist, weil es einfacher ist, mit einer normalisierten Mantisse mit einem versteckten (optionalen) Bit zu arbeiten.
Die zweite Frage ist, warum die Bestellung mit einem Offset gespeichert wird und nicht anders. Meine Antwort ist, weil es in diesem Fall einfach ist, die Module von zwei PTs als ganze Zahlen zu vergleichen, mit anderen Methoden ist es komplizierter.
Die dritte Frage ist, warum das negative Vorzeichen eher durch Eins als durch Null codiert wird, weil es dann möglich wÀre, die beiden Punkte einfach als ganze Zahlen zu vergleichen. Meine Antwort lautet: Ich weià es nicht. Es ist nur "hier wird es so akzeptiert".
Dritter Teil - Erforderliche ErklÀrungen
Im vorigen Absatz konnte ich unverstĂ€ndliche Begriffe nennen, also ein wenig ĂŒber die Darstellung von Zahlen. NatĂŒrlich sind sie unterschiedlich, sonst wĂ€re es nicht nötig, sie zu diskutieren. Wir stellen sofort fest, dass es im Speicher von MK (dasselbe gilt fĂŒr Computer, obwohl ich die modernsten Architekturen nicht so kategorisch finde - sie sind so kompliziert, dass alles zu erwarten ist) keine Zahlen gibt, sondern nur elementare Speichereinheiten - Bits, die in Gruppen zusammengefasst sind Bytes und weiter in Worte. Wenn wir ĂŒber die Darstellung einer Zahl sprechen, bedeutet dies, dass wir einen Satz von Bits einer bestimmten LĂ€nge auf die eine oder andere Weise interpretieren, dh ein Gesetz festlegen, nach dem wir eine bestimmte Zahl finden können, die einem bestimmten Satz von Bits entspricht, und nicht mehr.
Es können unzĂ€hlige solcher Gesetze erfunden werden, aber einige von ihnen haben eine Reihe nĂŒtzlicher Eigenschaften hinsichtlich der DurchfĂŒhrung verschiedener Operationen, so dass sie in der Praxis hĂ€ufiger angewendet werden. Eine dieser Eigenschaften, die beispielsweise implizit impliziert wird, ist Determinismus, und die andere ist die UnabhĂ€ngigkeit von der Umwelt - Eigenschaften, die auf den ersten Blick offensichtlich sind, obwohl es Nuancen gibt. Andere Eigenschaften der Art der Eins-zu-Eins-Korrespondenz sind bereits Gegenstand der Diskussion und finden nicht immer in einer konkreten Darstellung statt. Das Thema der Darstellung von Zahlen an sich ist ungewöhnlich faszinierend: FĂŒr Knut (in Band 2) ist es vollstĂ€ndig offenbart, so dass es ĂŒber die Tiefen hinausgeht und wir ĂŒber die OberflĂ€che gehen.
Unter der Annahme, dass die Menge der Bits eine LĂ€nge n hat (wir nummerieren sie in einer Reihe von 0 bis n-1) und einheitlich mit einem Schritt von 2 gewichtet werden und das niedrigstwertige Bit (mit der Nummer 0) eine Gewichtung von 1 hat (was im Allgemeinen ĂŒberhaupt nicht notwendig ist, wir nur Wir haben uns an solche Dinge gewöhnt und sie scheinen uns offensichtlich zu sein. Wir erhalten eine binĂ€re Darstellung der Zahl, in der die Reduktionsformel so aussieht: die Zahl, die durch die Menge der Bits
(2) = (0)*2^0 + (1)*2^1 + ... + (-1)*2^(-1)
angezeigt wird
(2) = (0)*2^0 + (1)*2^1 + ... + (-1)*2^(-1)
oder in kaskadierter Form
2() = (0)+2*((1)+2*(...+2*((-1))..)))
, im Folgenden bezeichnet B (k) ein Bit mit der Nummer k Eine andere Darstellung legt keine EinschrĂ€nkungen fĂŒr die Position der Bytes der Zahl im Speicher fest, aber es wĂ€re logischer, das niedrige Byte in den unteren Adressen zu platzieren (so einfach und natĂŒrlich habe ich das âewige Argument der Slawen untereinanderâ gelöst, welches Ende bequemer ist, um ein Ei zu zerbrechen).
Mit dieser Interpretation eines Satzes von Bits der LĂ€nge n (= 8) erhalten wir eine Darstellung fĂŒr Zahlen von 0 bis (2 ^ n) -1 (= 255) (im Folgenden wird in Klammern ein spezifischer Wert fĂŒr einen Satz von 8 Bits angegeben), der eine bemerkenswerte Anzahl aufweist und nĂŒtzliche Eigenschaften, weshalb es weit verbreitet ist. Leider hat es auch eine Reihe von Nachteilen, von denen einer darin besteht, dass wir in einem solchen Datensatz im Prinzip keine negativen Zahlen darstellen können.
Sie können eine Vielzahl von Lösungen fĂŒr dieses Problem anbieten (die Darstellung negativer Zahlen), von denen es auch praktische Bedeutung gibt. Sie sind unten aufgefĂŒhrt.
Eine Darstellung mit einem Versatz wird durch die Formel H = N2 (n) - Versatz (C) beschrieben, wobei N2 die in binĂ€rer Notation mit n Bits erhaltene Zahl ist und C ein vorgewĂ€hlter Wert ist. Dann stellen wir Zahlen von 0-C bis 2 ^ (n) -1-C dar, und wenn wir C = 2 ^ (n-1) -1 (= 127) wĂ€hlen (dies ist völlig optional, aber sehr praktisch), dann wir erhalten den Bereich von 0- (2 ^ (n-1) -1) (= - 127) bis 2 ^ (n-1) (= 128). Der Hauptvorteil dieser Darstellung ist die Monotonie (darĂŒber hinaus Zunahme) ĂŒber das gesamte Intervall. Es gibt auch Nachteile, unter denen wir die Asymmetrie hervorheben (es gibt andere, die mit der KomplexitĂ€t der DurchfĂŒhrung von Operationen an der Zahl in dieser Darstellung zusammenhĂ€ngen), aber die Entwickler des IEEE 457-Standards (dies ist der Standard fĂŒr) PT) hat diesen Fehler in eine Tugend verwandelt (indem ein zusĂ€tzlicher Wert verwendet wurde, um die Situation nan zu kodieren), was erneut die Treue des coolen Sprichworts unterstreicht: âWenn Sie höher als der Gegner sind, dann ist dies Ihr Vorteil. Wenn der Gegner gröĂer ist als Sie, ist dies auch Ihr Vorteil. â
Da die Gesamtzahl der möglichen Kombinationen einer beliebigen Anzahl von Bits gerade ist (wenn Sie keine aus religiösen GrĂŒnden verbotenen Kombinationen haben), ist die Symmetrie zwischen positiven und negativen darstellbaren Zahlen grundsĂ€tzlich nicht erreichbar (oder vielmehr erreichbar, aber unter bestimmten zusĂ€tzlichen Bedingungen, ĂŒber die mehr) .
Darstellung in Form eines direkten Codes, wenn eines der Bits (höchstwertig) das codierte Vorzeichen der Zahl H = (-1) ^ B (n-1) * P2 (n-1) darstellt, hat einen Bereich von 0- (2 ^ (n-1) -1) (= -127) bis 2 ^ (n-1) -1 (= 127). Es ist interessant festzustellen, dass ich gerade die grundsĂ€tzliche Unmöglichkeit der Symmetrie erklĂ€rt habe, und hier ist es klar: Die maximal darstellbare positive Zahl ist gleich dem Modul der minimal darstellbaren negativen Zahl. Dieses Ergebnis wird durch zwei Darstellungen fĂŒr Null (00 ... 00 und 10 ... 00) erreicht, was normalerweise als Hauptnachteil dieser Methode angesehen wird. Dies ist wirklich ein Nachteil, aber nicht so schrecklich, wie allgemein angenommen wird, da es bedeutendere gibt, die seine Verwendung einschrĂ€nken.
Die inverse Codedarstellung, wenn wir in der direkten Darstellung alle Bits des Wertes fĂŒr negative Zahlen H = (1-B (n-1)) * P2 (n-1) + B (n-1) * (2 ^ (n) invertieren -1) -CH2 (n-1)) - dies ist aus der Definition, Sie können eine viel verstĂ€ndlichere Formel machen H = Ch2 (n-1) -B (n-1) * (2 ^ (n-1) -1), Dies erlaubt uns, Zahlen von 0-2 ^ (n-1) +1 (= - 127) bis 2 ^ (n-1) -1 (= 127) darzustellen. Es ist ersichtlich, dass diese Darstellung verschoben ist, aber die Verschiebung sich schrittweise Ă€ndert, was diese Darstellung nicht monoton macht. Wieder haben wir zwei Nullen, was nicht sehr beĂ€ngstigend ist. Das Auftreten einer zirkulĂ€ren Ăbertragung wĂ€hrend der Addition ist viel schlimmer, was bestimmte Probleme bei der Implementierung von ALU verursacht.
Um den letzten Nachteil der vorherigen Darstellung zu beseitigen, ist es ungewöhnlich einfach, den Versatz um eins zu Ă€ndern. Dann erhalten wir = = 22 (n-1) -B (n-1) * 2 ^ (n-1) und können Zahlen von 0-2 ^ ( n-1) (= - 128) bis 2 ^ (n-1) -1 (= 127). Es ist leicht zu erkennen, dass die Darstellung asymmetrisch ist, aber Null ist eindeutig. Wesentlich interessanter ist die folgende Eigenschaft: "Es ist völlig offensichtlich, dass" keine RingĂŒbertragung fĂŒr eine Operation vom Additionstyp auftritt, was (zusammen mit anderen angenehmen Merkmalen) der Grund fĂŒr die universelle Verteilung dieser speziellen Methode zum Codieren negativer Zahlen ist.
Lassen Sie uns eine Tabelle mit interessanten Werten fĂŒr verschiedene Methoden zur Codierung von Zahlen erstellen, die mit H den Wert 2 ^ (n-1) (128) bezeichnen.
Bits | 00..00 | 11/01 | 10..00 | 11.11 |
---|
H (n) | 0 | H-1 (127) | H (128) | 2 * H-1 (255) |
H (n-1) | 0 | H-1 (127) | 0 | H-1 (127) |
Offset. N. | -H + 1 (-127) | 0 | 1 | H (128) |
Direkt | 0 | H-1 (127) | 0 | -H + 1 (-127) |
Umkehren | 0 | H-1 (127) | -H + 1 (-127) | 0 |
ErgÀnzung | 0 | H-1 (127) | -H (-128) | -1 |
Zum Abschluss des Themas geben wir Grafiken fĂŒr die aufgelisteten Darstellungen, aus denen ihre Vor- und Nachteile sofort ersichtlich sind (natĂŒrlich erinnert nicht alles, was einen an das interessante Sprichwort erinnert: âDer Vorteil der grafischen Darstellung von Informationen ist visuell, es hat keine anderen Vorteileâ).
Teil 4 - Das ursprĂŒngliche Problem tatsĂ€chlich lösen (besser spĂ€t als nie).
Kleiner Exkurs
ZunĂ€chst wollte ich den PT im Hexadezimalformat drucken (und letztendlich habe ich es getan), aber ganz unerwartet / völlig unerwartet (ich musste ihn ersetzen) stieĂ ich auf das folgende Ergebnis. Was wird Ihrer Meinung nach als Ergebnis der AusfĂŒhrung der Operatoren gedruckt:
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);
Beachten Sie auch die folgende Konstruktion und deren Ergebnis:
printf("%x %x %f",1.0,1.0);
Ich werde dieses PhÀnomen nicht erklÀren, "klug genug".
Wie drucken wir jedoch die hexadezimale Darstellung von PT korrekt? Die erste Lösung liegt auf der Hand - Vereinigung, die zweite ist jedoch fĂŒr einzeilige Fans printf ("% x", * ((int *) (& f))); (Ich entschuldige mich, wenn jemand durch zusĂ€tzliche Klammern beleidigt wurde, aber ich konnte und wollte mich nie an die PrioritĂ€ten von Operationen erinnern, insbesondere wenn man bedenkt, dass die Klammern keinen Code generieren, also werde ich das Gleiche tun). Und hier ist die Lösung der Aufgabe: Wir sehen eine Zeichenfolge, 0x45678, die die gewĂŒnschte Nummer fĂŒr uns eindeutig bestimmt, aber so, dass wir (ich weiĂ definitiv nichts ĂŒber Sie) nichts VerstĂ€ndliches ĂŒber diese Nummer sagen können. Ich denke, dass der Akademiker Karnal, der auf einen Fehler im Lochband mit dem Quellcode hĂ€tte hinweisen können, sich mit dieser Aufgabe befasst hĂ€tte, aber nicht jeder ist so weit fortgeschritten, also werden wir fortfahren.
Wir werden versuchen, Informationen in verstÀndlicherer Form zu erhalten.
Dazu kehren wir zum Format des PT zurĂŒck (im Folgenden betrachte ich nur float), bei dem es sich um eine Menge von Bits handelt, aus denen Sie (nach bestimmten Regeln) drei SĂ€tze von Bits extrahieren können, um drei Zahlen darzustellen - Vorzeichen, Mantisse (m) und Reihenfolge (p) und die gewĂŒnschte Zahl, die durch diese Zahlen codiert wird, wird durch die folgende Formel bestimmt: Cs * Chm * Chn. Hier bezeichnen die Symbole die Zahlen, die durch den entsprechenden Satz von Bits dargestellt werden. Um die gewĂŒnschte Zahl zu finden, mĂŒssen wir daher die Gesetze kennen, nach denen wir diese drei SĂ€tze aus dem ursprĂŒnglichen Satz von Bits extrahieren, sowie die Art der Codierung fĂŒr jeden von ihnen.
Bei der Lösung dieses Problems wenden wir uns dem IEEE-Standard zu und stellen fest, dass das Vorzeichen ein (Ă€lteres) Bit der ursprĂŒnglichen Menge und der Formel fĂŒr die Codierung von Cs = (- 1) ^ B (0) ist. Die Reihenfolge belegt die nĂ€chsten 8 hohen Bits, wird in Code mit einem Versatz von 127 geschrieben und stellt eine Zweierpotenz dar, dann ist Cn = 2 ^ (C2 (8) -127). Mantisse nimmt die nĂ€chste Reihenfolge von 23 Stellen an und reprĂ€sentiert die Zahl Chm = 1 + Ch2 (23) / 2 ^ 23.
Jetzt haben wir alle notwendigen Daten und können die Aufgabe vollstÀndig lösen - eine Zeichenfolge mit Zeichen zu erstellen, die bei einer bestimmten Lesart eine Zahl darstellt, die der codierten entspricht. Dazu sollten wir durch einfache Operationen die obigen Zahlen extrahieren und sie dann ausdrucken und die erforderlichen Attribute bereitstellen. Wir gehen davon aus, dass wir eine Ganzzahl mit nicht mehr als 32 Bit in eine Zeichenfolge konvertieren können, dann ist dies völlig unkompliziert.
Leider stehen wir erst am Anfang der Reise, da nur wenige Leser dieses Beitrags im Datensatz â+ 1.625 * 2 ^ 3â die unglĂŒckliche Zahl erkennen, die durch die hĂ€ufigere Dezimalstelle â13â codiert wird, und im Datensatz â1.953125 * 2â raten ^ 9 âdie einfachen" 1E3 "oder" 1 * 10 ^ 3 "oder die sehr vertrauten" 1000 "sind in der Lage, Einheiten von Menschen im Allgemeinen, ich gehöre definitiv nicht zu ihnen. Es ist seltsam, wie es passiert ist, denn wir haben die erste Aufgabe abgeschlossen, die erneut zeigt, wie sorgfĂ€ltig Sie die Formulierungen behandeln sollten. Und der Punkt ist nicht, dass die Dezimalschreibweise besser oder schlechter ist als die BinĂ€rschreibweise (in diesem Fall basiert Deuce auf dem Grad), sondern dass wir es gewohnt sind, seit der Kindheit zu dezimalisieren und Menschen neu zu erstellen, viel schwieriger als das Programm, also werden wir unsere geben Eintritt in das Vertraute.
Aus mathematischer Sicht haben wir eine einfache Operation - es gibt einen Datensatz PT = (- 1) ^ s * m * 2 ^ n, und wir mĂŒssen ihn in die Form PT = (-1) s '* m' * 10 ^ n 'konvertieren. Wir setzen die Lösungen s '= s', m '= m, n' = n * log (2) gleich, transformieren und erhalten (eine der möglichen Optionen). Wenn wir die Klammern weglassen, dass mit einer explizit irrationalen Zahl multipliziert werden muss (dies kann geschehen, wenn die Zahl rationalisiert ist, aber wir werden spĂ€ter darĂŒber sprechen), scheint das Problem gelöst zu sein, bis wir die Antwort sehen, denn wenn der Datensatz â+1.953125â lautet * 2 ^ 9 "scheint uns dunkel, der Rekord" + 1.953125 * 10 ^ 2.70927 "ist noch weniger akzeptabel, obwohl es anscheinend nirgendwo schlimmer war.
â 10 '= * 10^{ * lg(2)}, '= [ * lg(2)], . (1.953125*10^0.7 0927)*10^2=«10*10^2», , , .
, :
- () (lg(2)) ( );
- ( );
- (10) (...);
- (« , ...»).
, , , 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,
â , , =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. , , , , , , , .