Zahl umdrehen?

OP #7415253
Lesenswert?

Hi

Ich habe eine 64 Bit Binärzahl, in der steht leider das niederwertigste Bit links, das höchstwertige rechts.

Bevor ich jetzt anfange, jedes Bit einzeln umzusortieren, frage ich erstmal: Gibt es eine einfache (mathematische) Möglichkeit, die ganze Zahl um 180° zu drehen?

Beitrag #7415276 wurde von einem Moderator gelöscht.
#7415277
Lesenswert?

1
uint64_t Flip ( uint64_t Zahl )
2
{
3
  uint64_t Temp = 0;
4
  for ( uint8_t i = 0; i<=64; i++ ){
5
   if ( Zahl & (1ul<<(64-i)) ) // Von links anfangen
6
    Temp |= (1ul<<i); // und rechts einsortieren
7
  }
8
  return Temp;
9
}

Das kann man noch weiter optimieren, aber verständlicher Code ist gut wartbar

#7415321
Lesenswert?

Norbert schrieb:

So vielleicht?

Geht auch, aber schlecht lesbar und schlecht wartbar.

Norbert schrieb:

for ( uint8_t i = 0; i<=64; i++ )

Off by one error!

Ja, leider verrissen :(, Informatiker 0-Problem

Hier nochmal in lesbar

1
uint64_t Flip ( uint64_t Zahl )
2
{
3
  uint64_t Temp = 0;
4
  for ( uint8_t i = 0; i<64; i++ ){ // Schleife läuft von 0...63
5
   if ( Zahl & (1ul<<(63-i)) ) // Von links anfangen
6
    Temp |= (1ul<<i); // und rechts einsortieren
7
  }
8
  return Temp;
9
}
Gast #7415356
Lesenswert?

Gibt es eine einfache (mathematische) Möglichkeit, die ganze Zahl um 180° zu drehen?

Klar einfach auf dem karierten Mathepapier die Zahlen in umgekehrter Schreibweise (reverse order) hinschreiben ....

Ohne zu wissen, welche Möglichkeiten/Tools du hast/kennst/dich_nicht_zierst_zu_benutzen kann man die Frage nicht beantworten!

Manche Prozessoren haben einen Maschienenbefehl um die Reihenfolge zu tauschen (DSP, Cortex -> RBIT). Siehe Anhang.

Dann kann man sich mit Schiebe/Rotationsbefehlen was basteln, vielleicht kann man mit einem Barrel-shifter was beschleunigen

Für PLD schreibt am einfach die Zeile sig_a(31 downto 0) <= sig_b(0 to 31) und die tools basteln die Umverdrahtung.

Ein Blick in die maschinennahen Bibliotheken (bspw.: CMSIS) kann auch nicht schaden.

Also hilf dir selbst, dann hilft dir Gott!

Angehängte Dateien:
Gast #7415420
Lesenswert?

Mi N. schrieb:

Ingo L. schrieb:

Je nach Architektur

Die ja streng geheim ist...

Es geht wohl um PicoMite (BASIC) und den RP2040

Na dann ist es doch einfach, weil "MMBasic allows the embedding of compiled C programs for high performance functions"

Also einfach den vorgeschlagenen ARM-maschinenbefehl ("RBIT") als online in C einbinden, kompilieren und dem Basic vor die Füße werfen. Ferdsch! Beitrag "Re: Zahl umdrehen?"

Wobei, der RP2040 als CortexMo doch 32bit lange Register hat?!

#7415437
Lesenswert?

straight code wenn der Prozessor Byte-Orientiert ist (ungetestet):

1
typedef unsigned char BYTE;
2

3
BYTE revBYTE(BYTE b) 
4
{
5
/*z.B. so. Der Compiler weiss, was er daraus macht */
6
  b = (b & 0xf0)>>4 | (b & 0x0f)<<4;
7
  b = (b & 0xcc)>>2 | (b & 0x33)<<2;
8
  b = (b & 0xaa)>>1 | (b & 0x55)<<1;
9
}
10

11
void foo(void)
12
{
13
BYTE *pIn  = (BYTE*) &DeineZahlIn;
14
BYTE *pOut = (BYTE*) &DeineZahlOut + sizeof(DeineZahlIn);
15
BYTE *pInEnd= pIn                  + sizeof(DeineZahlIn);
16

17
   do {*--pOut = revBYTE(*pIn++);} while(pIn < pEnd);
18
}
#7415496
Lesenswert?

Peter N. schrieb:

Gibt es eine einfache (mathematische) Möglichkeit, die ganze Zahl um 180° zu drehen?

mathematisch nicht, aber gewusst wie.

"Mathematisch" könnte man sich an Haskell versuchen. https://stackoverflow.com/questions/19725292/how-to-reverse-an-integer-in-haskell Der grobe Ansatz da ist aber u.a. die "Zahl" in eine Liste zu verwandeln, und diese dann umdrehen - was sich leichter automatisieren wie auch gezielter angehen lässt.

Ja nach Hardware hat man noch andere Möglichkeiten, Goodies hier, Goodies da - nur dann muss man die Hardware kennen.

Kenn man die Hardware nicht, könnte man sich zumindest mal den den Asm-Code vom (c-)Compiler (s.o.) genauer ansehen.

Um Verwirrungsalarm aus dem Weg zu gehen, sollte man sich mehrere Lösungsansätze ansehen, u.a. auch https://www.techiedelight.com/de/inplace-rotate-matrix-180-degrees/

#7415522
Lesenswert?

DSGV-Violator schrieb:

Also einfach den vorgeschlagenen ARM-maschinenbefehl ("RBIT") als online in C einbinden, kompilieren und dem Basic vor die Füße werfen.

Ich befürchte allerdings das der CortexM0+ ein »RBIT« leider gar nicht in seinem Repertoire hat…

Allerdings verfügt er über PIOs samt Assembler, da könnte man mal hinein schauen. Aber vorher anschnallen, …

#7415566
Lesenswert?

Moin,

Fritz G. schrieb:

Dergute W. schrieb:

Einfach mit einer Lookup-Table arbeiten. Passt genau in 131072 PiByte

Wenn man eine Lookup-Tabelle für 8 Bit-Zahlen erstellt, belegt diese ganze 256 Bytes. Soviel Platz im ROM sollte sein. Dann kann man die 64bit-Zahl in 8 Schritten à 1 Byte umdrehen. Das ginge deutlich schneller als die 64Bits einzeln zu schieben.

Ja, wenns ums Platzsparen geht, dann wird man mit einer 1bit LUT noch viel mehr sparen koennen...

scnr, WK

#7415580
Lesenswert?

Ich würde es mit einer Mischlösung von Tabellenumrechnung und Bytevertausch versuchen:

1
#include <stdio.h>
2
#include <stdint.h>
3

4
static const uint8_t bitReverseTable[256] = {
5
  0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 
6
  0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
7
  0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8,
8
  0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
9
  0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4,
10
  0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
11
  0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC,
12
  0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
13
  0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2,
14
  0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
15
  0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA,
16
  0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
17
  0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6,
18
  0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
19
  0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 
20
  0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
21
  0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1,
22
  0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
23
  0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9,
24
  0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
25
  0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5,
26
  0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
27
  0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED,
28
  0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
29
  0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3,
30
  0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
31
  0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB,
32
  0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
33
  0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7,
34
  0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
35
  0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF,
36
  0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
37
};
38

39
uint64_t reverse64(uint64_t num) {
40

41
    union RN {
42
      uint64_t val64;
43
      uint8_t val8[8];
44
    }
45
     
46
    RN rn_i,rn_o;
47
 
48
    rn_i.val64 = num;
49

50
    rn_o.val8[0] = bitReverseTable[rn_i.val8[7]];
51
    rn_o.val8[1] = bitReverseTable[rn_i.val8[6]];
52
    rn_o.val8[2] = bitReverseTable[rn_i.val8[5]];
53
    rn_o.val8[3] = bitReverseTable[rn_i.val8[4]];
54
    rn_o.val8[4] = bitReverseTable[rn_i.val8[3]];
55
    rn_o.val8[5] = bitReverseTable[rn_i.val8[2]];
56
    rn_o.val8[6] = bitReverseTable[rn_i.val8[1]];
57
    rn_o.val8[7] = bitReverseTable[rn_i.val8[0]];
58

59
    return rn_o.val64;
60
}

ACHTUNG: Bit- und Byteorder kann von der Prozessorplattform abhängig sein!

#7415894
Lesenswert?

Norbert schrieb:

Allerdings verfügt er über PIOs samt Assembler, da könnte man mal hinein schauen. Aber vorher anschnallen, …

Ach watt, völlig trivial. Das ist ein Fünfzeiler, davon nur drei Instruktionen. pull block, mov ISR, OSR (mit Operation "bit-reverse"), push noblock.

Sprich: wenn die PIO mit Systemtakt läuft, braucht das Teil drei CPU-Takte für die Operation.

#7415901
Lesenswert?

C-hater schrieb:

Norbert schrieb:

Allerdings verfügt er über PIOs samt Assembler, da könnte man mal hinein schauen. Aber vorher anschnallen, …

Ach watt, völlig trivial. Das ist ein Fünfzeiler, davon nur drei Instruktionen. pull block, mov ISR, OSR (mit Operation "bit-reverse"), push noblock.

Sprich: wenn die PIO mit Systemtakt läuft, braucht das Teil drei CPU-Takte für die Operation.

Mist, übersehen: es ging um eine 64Bit-Zahl. Dann wird's doch etwas länglicher, dann sind's sieben Operationen (also sieben Takte):

pull block mov X, Bit-Revers OSR pull block mov ISR, Bit-Revers OSR push noblock mov ISR, X push noblock

Beitrag #7415903 wurde von einem Moderator gelöscht.
Beitrag #7415906 wurde von einem Moderator gelöscht.
#7415908
Lesenswert?

Peter N. schrieb:

Bevor ich jetzt anfange, jedes Bit einzeln umzusortieren, frage ich erstmal:

Naja. Die Frage ist schlicht, wie schnell es denn WIRKLICH sein muss? Du musst sicher NICHT die Zahl in 1us umdrehen und auch nicht 1M davon.

Wenn der triviale Ansatz der Bitschieberei schnell genug ist, nimm ihn und wende dich den wirklich wichtigen und interessanten Aufgaben deines Projektes zu.

https://www.mikrocontroller.net/articles/AVR-GCC-Codeoptimierung#Prinzipien_der_Optimierung

Gilt sehr allgemein für viele Dinge, nicht nur in der Elektronik und Software.

#7415921
Lesenswert?

C-hater schrieb:

Welchen? 32Bit oder 64Bit? Und wo finde ich auf github irgendwelche Diskussionsforen?

github: MicroPython: Discussions Hab' erst eine recht schnelle Python-Viper Routine geschrieben, danach eine Python-Thumb-Assembler Version und dann den schnellsten Vorschlag mit PIO Assembler und DMA gemacht. Der war - wie du schon geschrieben hattest - so trivial, das ihn nur erwähnt hatte.

War zwar nur 32bit, aber sinngemäß geht's natürlich mit beliebigen Bitweiten.

#7415943
Lesenswert?

Norbert schrieb:

War zwar nur 32bit, aber sinngemäß geht's natürlich mit beliebigen Bitweiten.

Ähem, nö. Mit den einfachen PIO-Programmen ist bei diesem konkreten Problem bei 96 Bit Schicht im Schacht. Liegt daran, dass die PIO halt nur vier Register hat, davon nur zwei "frei verwendbare" (hier natürlich als Cache für das Reverse der Long-Words).

128Bit oder gar 256Bit mit vMax zu reversen braucht eine Kombination aus PIO und ARM-Assembler.

Das ist dann schon nicht mehr ganz so trivial...

Beitrag #7415947 wurde von einem Moderator gelöscht.
#7416010
Lesenswert?

Bei max. 2048b: Eine PIO-SM hat jeweils vier ›WORD‹ FIFO kann also 8*32b = 256b ohne weiteres halten. Vier davon sofort verarbeiten und vier im FIFO halten. Acht SM können so 2048b entgegen nehmen.

Die Daten müssen ja sowieso irgendwo stehen, bevorzugt in einem array.

Man kann 64 ›WORD‹ aus dem array lesen und ungebremst in die PIO-FIFOs schieben. Dann liest man ungebremst 64 ›WORD‹ und schreibt sie rückwärts in das array zurück.

Wenn größer 2048b, dann kann man auch noch den Stapel nutzen. Wenn der nicht reicht, wird's akademisch… ;-)

#7416206
Lesenswert?

Norbert schrieb:

Eine PIO-SM hat jeweils vier ›WORD‹ FIFO kann also 8*32b = 256b ohne weiteres halten. Vier davon sofort verarbeiten und vier im FIFO halten.

Die FIFOs sind aber leider aus der PIO heraus nicht wahlfrei zugänglich. Deswegen heißen die Befehle für den Zugriff darauf auch PUSH/POP. Sprich: die "Drehung" der Daten auf der Ebene von 32Bit-Wörtern ist nur über Zwischenspeicherung in der PIO möglich. Deswegen ist deren Registerzahl hier der limitierende Faktor.

Die Daten müssen ja sowieso irgendwo stehen, bevorzugt in einem array.

Die PIO kann eben nicht wahlfrei darauf zugreifen, sondern nur als FIFO. Also nix mit Array.

Acht SM können so 2048b entgegen nehmen.

Ja klar, mit mehr PIO-Instanzen geht natürlich mehr als mit nur einer.

#7416249
Lesenswert?

C-hater schrieb:

Die FIFOs sind aber leider aus der PIO heraus nicht wahlfrei zugänglich.

Wenn du Zahlen größer ›WORD‹ (sagen wir mal 8*32bit) in einem Speicherbereich liegen hast, dann müssen die sowieso in 32bit Stücken in den FIFO geschoben werden. Geht nicht anders. Bei einem zu spiegelnden 256bit Wert kann man mit einem ›mov‹ index 0 und acht ›ldr‹ RAM[index] ›add‹ index 1 ›str‹ TX_FIFO Befehlssequenzen ohne zu warten in den (Eingangs-)FIFO schreiben. Die ersten vier Werte holt sich das PIO-Programm direkt ab, reversiert und schreibt sie in den (Ausgangs-)FIFO. Der ›stalled‹ dann, weil voll. Die letzen vier Werte stehen jetzt noch im (Eingangs-)FIFO. Alles ist randvoll.

Jetzt müssen die Werte gelesen werden und zurück in's RAM. Also wieder: ›mov‹ index 0 Acht mal ›ldr‹ RX_FIFO ›add‹ index 1 ›str‹ RAM[index]

Sobald ein Wert aus dem RX_FIFO gelesen wird, rutscht den nächste automatisch aus dem TX_FIFO, wird gespiegelt und in den RX_FIFO geschoben. Damit sind schließlich die acht ›WORD‹ sowohl gespiegelt als auch (im RAM) vertauscht. Da braucht man noch nicht mal X und Y, ISR und OSR reichen aus. Und nur einen einzigen PIO-Assembler Befehl.

#7416356
Lesenswert?

C-hater schrieb:

Also offensichtlich: TROLL. Aber sowas von.

Und vermutlich noch nicht mal das Handbuch der CPU gelesen. Naja, er dachte wohl, es gib irgendeinen mathematischen Trick.

Tatsächlich beziehen sich 180 Grad eher auf eine Kreisvorstellung. Wenn man das auf eine Uhr anwendet, braucht man praktisch nur die 12 nach unten ziehen. Wenn man 64 Bit kreisförmig anordnet, kann man das auch machen - per Rotation. In diesem Fall in 32 Schritten. Da wäre dann aber nur der obere Teil mit dem unteren vertauscht. Möglicherweise nicht das gesuchte Ergebnis Ganz abgesehen davon, dass das nicht die schnellste Methode wäre (s.o.).

Gestern hatte ich noch zwei Links gefunden, die nur die Grundlagen bearbeiten, und deswegen hatte ich mir die erspart. Aber nach den vielen guten Hinweisen oben ist mir das jetzt auch egal ;)

https://www.youtube.com/watch?v=wXGZ7o9HNss https://www.geeksforgeeks.org/c-program-for-program-for-array-rotation-2/

OP #7416364
Lesenswert?

Ich hätte nie gedacht, daß so eine für mich einfache Frage, soviele Reaktionen hervorruft...

Norbert schrieb:

Es darf mit einiger Wahrscheinlichkeit angenommen werden, das ein Pi Pico mit RP2040 (also ARM Cortex-M0plus) zum Einsatz kommt.

Genau so ist es. Aber ich wollte allgemeine und einfache Antworten, die ich in PicoMite umsetzten kann, deshalb keine näheren Angaben. Mit den Kernen des RP2040 kann ich (noch lange) nichts anfangen...

Ich habe das Problem auf die unelegante Weise gelöst: Zahl in String umgewandelt und Zeichen für Zeichen umgekehrt in einen zweiten String kopiert.

(Firma: Vescomp) #7416431
Lesenswert?

Peter N. schrieb:

Ich habe das Problem auf die unelegante Weise gelöst: Zahl in String umgewandelt und Zeichen für Zeichen umgekehrt in einen zweiten String kopiert.

Und warum dann nicht noch komplizierter? Du weisst doch, daß nix langsamer ist, als hantieren mit Strings? Und 64 Mal ein integer schieben ist immer noch um ein vielfacher schneller.

1
#include <stdio.h>
2
#include <stdint.h>
3

4
uint64_t DreheBitsUm(uint64_t zahl) {
5
    uint64_t umgedreht = 0;
6
    uint64_t nn = zahl;
7
    for(;nn;nn >>= 1) {
8
        umgedreht <<= 1;
9
        umgedreht += (nn & 1);
10
    }
11
    return umgedreht;
12
}
13

14
int main() {
15
uint64_t xx;
16
  xx = DreheBitsUm(0xdeadbeef);
17
  printf("Umgedreht = 0x%X", xx);
18
  return 0;
19
}

Und dann vergleiche mal um wieviel schneller das ist...

(Firma: Vescomp) #7416457
Lesenswert?

Mario M. schrieb:

Marc V. schrieb:

for(;nn;nn >>= 1) {

1
    umgedreht <<= 1;
2

3
    umgedreht += (nn & 1);
4

5
}

Schnell aber falsch. Wenn man 0x1 rein schickt kommt kein 0x80000000 raus.

Lol.

1
uint64_t DreheBitsUm(uint64_t zahl) {
2
//*********************************
3
    if (zahl == 1) return 0x80000000;
4
//*********************************
5
    uint64_t umgedreht = 0;
6
    uint64_t nn = zahl;
7
    for(;nn;nn >>= 1) {
8
        umgedreht <<= 1;
9
        umgedreht += (nn & 1);
10
    }
11
    return umgedreht;
12
}

Zufrieden?

Beitrag #7416530 wurde von einem Moderator gelöscht.
Beitrag #7416542 wurde von einem Moderator gelöscht.
#7416582
Lesenswert?

Marc V. schrieb:

Peter N. schrieb:

Ich habe das Problem auf die unelegante Weise gelöst: Zahl in String umgewandelt und Zeichen für Zeichen umgekehrt in einen zweiten String kopiert.

Und warum dann nicht noch komplizierter?

Daß es beim TO um PicoMite geht, sollte mittlerweile jedem klar sein. Dann gib ihm bitte auch ein Beispiel in BASIC, damit er etwas damit anfangen kann. Daß irgendetwas ganz schnell gehen müßte, war nicht die Frage. Nur steigert man sich hier gerne dahin, daß maximal ein CPU-Takt zulässig sei.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren