Forum: PC-Programmierung IntToHex function


von Martin M. (ats3788)


Lesenswert?

Hallo
kennt jemand einen Source Code um die funktion
IntToHex zu bewerkstelligen. Ich dachte dazu kann ich einiges Googeln
aber alles was ich fand war nicht so brauchbar.
Das hatte ich gefunden nur da ist amp undefiniert.

static char hex[] = { '0', '1', '2', '3', '4', '5', '6', '7',
'8', '9' ,'A', 'B', 'C', 'D', 'E', 'F' };

//The function that performs the conversion. Accepts a buffer with 
"enough space" TM
//and populates it with a string of hexadecimal digits.Returns the 
length in digits
int uintToHexStr(unsigned int num, char* buff)
{
  int len = 0, k = 0;
  do//for every 4 bits
  {
    //get the equivalent hex digit
    buff[len] = hex[num& 0xF];
    len++;
    num >>= 4;
  } while (num != 0);
  //since we get the digits in the wrong order reverse the digits in the 
buffer
  for (; k<len / 2; k++)
  {//xor swapping
    buff[k] ^= buff[len - k - 1];
    buff[len - k - 1] ^= buff[k];
    buff[k] ^= buff[len - k - 1];
  }
  //null terminate the buffer and return the length in digits
  buff[len] = '\0';
  return len;
}

von asdf (Gast)


Lesenswert?

Hrmf. Ersetze "&amp;" durch "&". Dann wirds wohl gehen...

von imonbln (Gast)


Lesenswert?

Ohne weitere Vorgaben ist der Code denn du hast doch gar nicht so 
schlecht.
Gerade die erste while schleife macht doch eigentlich schon das was du 
willst.
Die for schleife würde ich umgehen indem ich vorher bswap aus den GNU 
Utils nutze oder was ähnliches dann stimmt auch die Byteorder wieder.

Alternativ kannst du auch sicher mit snprintf und passenden format 
specifier was machen. Dann könnte die Lösung ein Einzeiler sein. Aber da 
ich mir nicht sicher bin ob es eine Hausaufgabe ist, welche du gerade 
versuchst zu lösen, überlasse ich Dir das googlen wie snprintf das lösen 
kann.

von Martin M. (ats3788)


Lesenswert?

Danke ihr lieben, ich habe zwei Funktionen die ohne Problme mit C++ 
Builder funktionieren. Mit dem Arduino UNO
bekomme ich die richtige Länge des Strings, aber Buffer bleibt leer.
Leider kann man die Arduinos nicht debuggen.

uintToHexStr habe ich angepasst.

Der Code


static char hex[] = { '0', '1', '2', '3', '4', '5', '6', '7',
'8', '9' ,'A', 'B', 'C', 'D', 'E', 'F' };

//The function that performs the conversion. Accepts a buffer with 
"enough space" TM
//and populates it with a string of hexadecimal digits.Returns the 
length in digits
int uintToHexStr(unsigned int num, char* buff)
{
  int len = 0, k = 0;
  do//for every 4 bits
  {
    //get the equivalent hex digit
    int tmp = num & 0xF;
    buff[len] = hex[tmp];
    len++;
    num >>= 4;
  } while (num != 0);
  //since we get the digits in the wrong order reverse the digits in the 
buffer
  for (; k<len / 2; k++)
  {//xor swapping
    buff[k] ^= buff[len - k - 1];
    buff[len - k - 1] ^= buff[k];
    buff[k] ^= buff[len - k - 1];
  }
  //null terminate the buffer and return the length in digits
  buff[len] = '\0';
  return len;
}



unsigned int num_hex_digits(unsigned int n) {
  int ret = 0;
  while (n) {
    n >>= 4;
    ++ret;
  }
  return ret;
}

int IntToHex(uint32_t num, char* buffer)
{
  char *p = buffer;
  uint32_t pad = 0;
  uint32_t len = num_hex_digits(num);
  /* if odd number, add 1 - zero pad number */
  if (len % 2 != 0)
    pad = 1;

  /* move to end of number string */
  p += len + pad; //- 1;

  len = 0;
  *p = 0;
  *p--;
  while (num) {

    len++;
    int tmp = num & 0xF;
    if (tmp < 10)
      *p = tmp + '0';
    else
      *p = tmp + 'A' - 10;
    Serial.print("++++>");
    Serial.print(*p);
    num >>= 4;

    p--;
  }
  if (pad) {
    *p = '0';
  }

  return (len);
}

char Buffer[10];
uint32_t hex = 0x7988aa;

uintToHexStr(hex, Buffer);
  Serial.print("Hex Test ");
  Serial.println(Buffer);

von Dr. Sommer (Gast)


Lesenswert?


von Martin M. (ats3788)


Lesenswert?

Hallo das Problem war das unsigned int eine 16 bit integer ist und ich 
eine 32 bit zahl auflösen wollte.
so funktioniert das auch mit einem uno

static char hex[] = { '0', '1', '2', '3', '4', '5', '6', '7',
'8', '9' ,'A', 'B', 'C', 'D', 'E', 'F' };

//The function that performs the conversion. Accepts a buffer with 
"enough space" TM
//and populates it with a string of hexadecimal digits.Returns the 
length in digits
int uintToHexStr(uint32_t num, char* buff)
{
  int len = 0, k = 0;
  do//for every 4 bits
  {
    //get the equivalent hex digit
    uint32_t tmp = num & 0xF;
    buff[len] = hex[tmp];
    len++;
    num >>= 4;
  } while (num != 0);
  //since we get the digits in the wrong order reverse the digits in the 
buffer
  for (; k<len / 2; k++)
  {//xor swapping
    buff[k] ^= buff[len - k - 1];
    buff[len - k - 1] ^= buff[k];
    buff[k] ^= buff[len - k - 1];
  }
  //null terminate the buffer and return the length in digits
  buff[len] = '\0';
  return len;
}

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Martin M. schrieb:
> //since we get the digits in the wrong order reverse the digits in the
> buffer

Wie wäre es, die "digits" einfach gleich in der richtigen Reihenfolge 
anzuordnen?

von Nils Pipenbrinck (Gast)


Lesenswert?

Rufus Τ. F. schrieb:
> Wie wäre es, die "digits" einfach gleich in der richtigen Reihenfolge
> anzuordnen?

Wäre zu einfach, außerdem kann man dann den "tollen" XOR Trick nicht 
zeigen :-)

von Dr. Sommer (Gast)


Lesenswert?

Rufus Τ. F. schrieb:
> Wie wäre es, die "digits" einfach gleich in der richtigen Reihenfolge
> anzuordnen?

Dazu muss man aber wissen, wie viele Ziffern es sind, oder vorher Platz 
lassen. Folgendermaßen könnte man die Ziffern vorab zählen:
1
#include <type_traits>
2
#include <cstddef>
3
#include <iostream>
4
#include <limits>
5
#include <algorithm>
6
7
#ifdef __GNUC__
8
9
constexpr inline std::size_t leadingZeroes (unsigned char x) {
10
  return __builtin_clz (x);
11
}
12
13
constexpr inline std::size_t leadingZeroes (unsigned short x) {
14
  return __builtin_clz (x);
15
}
16
17
constexpr inline std::size_t leadingZeroes (unsigned int x) {
18
  return __builtin_clz (x);
19
}
20
21
constexpr inline std::size_t leadingZeroes (unsigned long x) {
22
  return __builtin_clzl (x);
23
}
24
25
constexpr inline std::size_t leadingZeroes (unsigned long long x) {
26
  return __builtin_clzll (x);
27
}
28
29
#endif
30
31
template <typename T>
32
std::size_t leadingZeroes (T x) {
33
  std::size_t c = 0;
34
  while ((x & (T { 1 } << (std::numeric_limits<T>::digits - 1))) == 0) { x <<= 1; ++c; }
35
  return c;
36
}
37
38
template <typename T>
39
typename std::enable_if<std::is_unsigned<T>::value, std::size_t>::type IntToHex (char* str, T val, std::size_t maxLen) {
40
  std::size_t digits = std::min<std::size_t> (maxLen-1, val == 0 ? 1 : (((std::numeric_limits<T>::digits-leadingZeroes (val)) + 3) / 4));
41
  char* p = str + digits;
42
  *p-- = 0;
43
  for (std::size_t i = 0; i < digits; ++i) {
44
    char d = static_cast<char> (val & 0xF);
45
    *p-- = d >= 10 ? (d - 10 + 'A') : (d + '0');
46
    val >>= 4;
47
  }
48
  return digits;
49
}
50
51
template <typename T, std::size_t N>
52
typename std::enable_if<std::is_unsigned<T>::value, std::size_t>::type IntToHex (char (&str) [N], T val) {
53
  return IntToHex (str, val, N);
54
}
55
56
int main () {
57
  char buf [40];
58
  std::uint32_t x;
59
  std::cin >> x;
60
  IntToHex (buf, x);
61
  std::cout << buf << std::endl;
62
}

Implementiert via templates, sodass es direkt für alle Integer-Typen 
funktioniert. Diese müssen aber vorzeichenlos sein. Dafür ist es so 
etwas effizienter und sicherer durch eingebaute Array-Bereichs-Prüfung. 
Funktioniert nur auf AVR-GCC und Arduino leider nicht, weil es da keine 
C++ Standard Bibliothek gibt.

von Gurl (Gast)


Lesenswert?

@Dr.Sommer : Waruuum?

von Gurl (Gast)


Lesenswert?

Für Controller mit effektivem Bitshifting(barrel shifter):

Leftshift anstelle von Rightshift
Höchtes Nibble runterholen mit >> (sizeof Zahl * 8 -4)  ,..  and 0xf
wenn Null, nicht in String einfügen, ansonst normal von links weg 
einfügen

von zitter_ned_aso (Gast)


Lesenswert?

Deine Hex-Darstellung wird ja in einem String gespeichert. Oben wurde 
schon erwähnt, dass du diese Funktion evtl. gar nicht brauchst.

imonbln schrieb:
> Alternativ kannst du auch sicher mit snprintf und passenden format
> specifier was machen. Dann könnte die Lösung ein Einzeiler sein.

Ich weiß nicht was Arduino Uno so alles kann, aber C kann jede dezimale 
Zahl in oktaler/hexadezimaler Darstellung problemlos darstellen.

Bsp.:
1
for(int i=0; i<20; i++)
2
          printf("dec: %d, oct: %#o, hex: %#x\n", i, i, i);
Oder halt mit snprintf in einem String ablegen

von PittyJ (Gast)


Lesenswert?

Ich hätte auch snprintf verwendet.

von Daniel F. (df311)


Lesenswert?

was ich im ganzen thread nicht gefunden habe ist eine (m.e. durchaus 
sinnvolle/wichtige) information:

wozu?

da das ganze unter pc programmierung gepostet ist, nehme ich mal an es 
handelt sich um eine plattform mit etwas mehr cojones als ein arduino 
(wurscht ob mini, nano, uno, ...)

: Bearbeitet durch User
von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Dr. Sommer schrieb:
> Dazu muss man aber wissen, wie viele Ziffern es sind,

Es ist recht üblich, Hexwerte mit führenden Nullen auszugeben. Und dann 
ist die Anzahl der Stellen implizit gegeben.

von Martin M. (ats3788)


Lesenswert?

Danke für die netten Antworten.
Leider geht das alles nicht was vorgeschlagen wurde Arduino UNO sprintf 
verarbeitet nur 16 bit Integer was größer ist wird abgeschnitten.

static char hex[] = { '0', '1', '2', '3', '4', '5', '6', '7',
'8', '9' ,'A', 'B', 'C', 'D', 'E', 'F' };

//The function that performs the conversion. Accepts a buffer with 
"enough space" TM
//and populates it with a string of hexadecimal digits.Returns the 
length in digits
int Int2Hex(uint32_t num, char* buff)
{
  int len = 0, k = 0;
  do//for every 4 bits
  {
    //get the equivalent hex digit
    uint32_t tmp = num & 0xF;
    buff[len] = hex[tmp];
    len++;
    num >>= 4;
  } while (num != 0);
  //since we get the digits in the wrong order reverse the digits in the 
buffer
  for (; k<len / 2; k++)
  {//xor swapping
    buff[k] ^= buff[len - k - 1];
    buff[len - k - 1] ^= buff[k];
    buff[k] ^= buff[len - k - 1];
  }
  //null terminate the buffer and return the length in digits
  buff[len] = '\0';
  return len;
}

Hierbei ist der uint32_t Teil wichtig.

von zitter_ned_aso (Gast)


Lesenswert?

Martin M. schrieb:
> Leider geht das alles nicht was vorgeschlagen wurde Arduino UNO sprintf
> verarbeitet nur 16 bit Integer was größer ist wird abgeschnitten.

na vermutlich wegen %d als format spezifier.

Bei den Datentypen aus inttypes.h sollte man auch dazugehörige Makros 
(für die  Eingabe/Ausgabe) benutzen (und nicht %d, %x usw..)
1
#include <stdio.h>
2
#include <stdlib.h>
3
#include <inttypes.h>
4
#include <limits.h>
5
6
7
int main(void){
8
  
9
    #define WHATEVER 100  
10
11
    uint32_t num=65535+1;
12
13
    char str[WHATEVER]; 
14
    sprintf(str, "dec: %" PRIu32 "\thex: %" PRIx32  "\n", num, num);
15
   
16
    puts(str); 
17
    
18
    return EXIT_SUCCESS;
19
}

Aber so ist es bestimmt noch einfacher und richtiger.

Dr. Sommer schrieb:
> Warum nicht einfachSerial.print(78, HEX);
> 
https://www.arduino.cc/reference/en/language/functions/communication/serial/print/
>
> oder
> String stringOne =  String(45, HEX);
> https://www.arduino.cc/en/Tutorial/StringConstructors

von Martin M. (ats3788)


Lesenswert?

Also
die Function die ich gepostet habe die funktioniert ohne Probleme
beim 8 bit UNO muss man einiges beachten der kann auch kein
Float ausgeben mit %f in printf oder sprintf da macht man es mit

dtostrf Beitrag "dtostrf funktion allgemein"

oder auch die Variablen. In PC C++ soll man nach Möglichkeit nur lokale 
Vars benutzen. Mit µMCU ist speicher ein hares gut und man muss Sparsam 
damit umgehen, so definiert man z.B. ein Text Buffer global
char Buffer[50]. Mehr Fallstricke fallen mir nicht ein währe vielleicht 
Sinnvoll in einem anderen Post.

Ein schönes Wochenende.

von Wolfgang (Gast)


Lesenswert?

Martin M. schrieb:
> Das hatte ich gefunden nur da ist amp undefiniert.

Das kommt davon, wenn man HTML-Texte kopiert.

von W.S. (Gast)


Lesenswert?

Martin M. schrieb:
> Ich dachte dazu kann ich einiges Googeln
> aber alles was ich fand war nicht so brauchbar.

Ach herrje, da hättest du besser dir die steinalte Lernbetty hier im 
Forum heruntergeladen. Hier mal ein Auszug:
1
const char HexChars[17]   = {"0123456789ABCDEF"};
2
3
/* ein Hex-Zeichen ausgeben */
4
void Nibble_Out (int aHex)
5
{ V24_CharOut (HexChars[aHex & 0x0F]); }
6
7
/* ein Byte als Hexa ausgeben */
8
void HexB_Out (byte aHex)
9
{ Nibble_Out (aHex >> 4);
10
  Nibble_Out (aHex);
11
}
12
13
/* ein Word als Hexa ausgeben */
14
void HexW_Out (word aHex)
15
{ HexB_Out (aHex >> 8);
16
  HexB_Out (aHex);
17
}
18
19
/* ein DWORD als Hexa ausgeben */
20
void HexL_Out (long aHex)
21
{ HexW_Out (aHex >> 16);
22
  HexW_Out (aHex);
23
}
24
25
/* ein QWORD als Hexa ausgeben */
26
void HexQ_Out (int64* aHex)
27
{ HexL_Out (*aHex >> 32);
28
  HexL_Out (*aHex);
29
}

So, reicht's?
Und wie man den ganzen Schmonzes in einen Puffer schreibt und dann die 
Länge oder nen Zeiger auf das folgende Nullbyte zurückliefert, überlasse 
ich dem TO als Fingerübung.

Und warum HexQ_Out hier einen Pointer anstelle des Values haben will, 
darf bei Bedarf ergrübelt werden. Stichwort SVC.

Manchmal frag ich mich, wieso Aufgaben dieses Levels eine verzweifelte 
Suche im Internet erforderlich machen.

W.S.

von W.S. (Gast)


Lesenswert?

Dr. Sommer schrieb:
> Dazu muss man aber wissen, wie viele Ziffern es sind,

Ach, noch nie von der RBI/RBO-Funkionalität gehört? Also Hardware kannst 
du auch nicht.

Nee, das Einfachste - sofern man sowas tatsächlich will (was ich 
bezweifle, das hat Rufus schon richtig zum Ausdruck gebracht) wäre, 
solange die auszugebenden Zeichen zu ignorieren, bis eines ungleich '0' 
kommt oder die Einerstelle erreicht ist. Eben RBI/RBO in Software.

W.S.

von Dr. Sommer (Gast)


Lesenswert?

W.S. schrieb:
> Ach, noch nie von der RBI/RBO-Funkionalität gehört? Also Hardware kannst
> du auch nicht.

Wo ging es in diesem Thread um Hardware?

W.S. schrieb:
> solange die auszugebenden Zeichen zu ignorieren, bis eines ungleich '0'
> kommt oder die Einerstelle erreicht ist.

Was aber nur geht, wenn man die Zeichen von links an produzieren kann. 
Was nur effizient geht, wenn man einen Barrel-Shifter hat (Mr. 
Hardware). Was aber auch schon alles gesagt wurde.

Was passiert eigentlich wenn man deine supertollen Code auf einer 
Plattform mit long=64 bit ausführt? Warum benutzt du auf ARM die 
unsägliche Windows-Nomenklatur mit word=16bit, wo ARM das schon ewig als 
32bit festlegt?

W.S. schrieb:
> Stichwort SVC.

Hast du im Ernst eine Reihe an Hex-Ausgabefunktionen als Syscall 
implementiert? Es aber nicht geschafft dabei mehr als ein 32bit-Word als 
Parameter zu übergeben? Mindestens 4 sollte man hinbekommen. Mehr auch 
mit etwas Stack-Pointer-Gymnastik.

von Dr. Sommer (Gast)


Lesenswert?

Für die Lästernden, hier eine Version welche von links an ausgibt und 
daher ohne Barrel-Shifter ineffizient ist:
1
#include <type_traits>
2
#include <cstddef>
3
#include <iostream>
4
#include <limits>
5
#include <algorithm>
6
#include <iterator>
7
8
template <typename T, typename Iter>
9
typename std::enable_if<std::is_unsigned<T>::value, std::size_t>::type IntToHex (Iter outIter, T val, std::size_t minDigits = 1) {
10
  static constexpr T mask = (T { 0xF } << (std::numeric_limits<T>::digits-4));
11
  
12
  std::size_t outDigits = (std::numeric_limits<T>::digits+3)/4;
13
  while ((val & mask) == 0 && outDigits > minDigits) {
14
    val <<= 4;
15
    --outDigits;
16
  }
17
  
18
  for (std::size_t i = 0; i < outDigits; ++i) {
19
    char d = static_cast<char> ((val >> (std::numeric_limits<T>::digits-4)) & 0xFF);
20
    *outIter = d >= 10 ? (d - 10 + 'A') : (d + '0');
21
    ++outIter;
22
    val <<= 4;
23
  }
24
  return outDigits;
25
}
26
27
struct UartOutput {
28
  UartOutput& operator ++ () { return *this; }
29
  UartOutput& operator * () {  return *this; }
30
  UartOutput& operator = (char x) {
31
    std::cout << "Simulierte UART-Ausgabe: " << x << std::endl;
32
    // UART_PutChar (x);
33
    return *this;
34
  }
35
};
36
37
int main () {
38
  std::uint64_t x;
39
  std::cin >> x;
40
41
  {  // Indirekte Ausgabe via Puffer
42
    char buf [40];
43
    buf [IntToHex (buf, x, 5)] = 0;
44
    std::cout << "Ausgabe via Puffer  : " << buf << std::endl;
45
  }
46
  {  // Direkte Ausgabe auf std::cout via Output-Iterator
47
    std::cout << "Ausgabe via Iterator: ";
48
    IntToHex (std::ostream_iterator<char> (std::cout), x, 5);
49
    std::cout << std::endl;
50
  }
51
  {  // Direkte Ausgabe auf UART via Output-Iterator
52
    IntToHex (UartOutput {}, x, 5);
53
  }
54
}
Funktioniert auch wieder dank Templates mit allen Integer-Typen auf 
allen Plattformen (solange die Größe ein Vielfaches von 4bit ist; für 
krumme Längen bräuchte es mehr Fallunterscheidungen). Es kann die 
Mindest-Länge der Ausgabe angegeben werden.

Zusätzlich erfolgt die Ausgabe über einen Output-Iterator. Der Aufrufer 
kann also selbst festlegen, ob in einen Puffer oder direkt in einen 
Output-Stream geschrieben werden soll. Als Beispiel wird in einen 
char-Buffer, direkt auf std::cout und einen Beispiel-Dummy-UART-Output 
geschrieben.

von W.S. (Gast)


Lesenswert?

Dr. Sommer schrieb:
> Hast du im Ernst eine Reihe an Hex-Ausgabefunktionen als Syscall
> implementiert? Es aber nicht geschafft dabei mehr als ein 32bit-Word als
> Parameter zu übergeben? Mindestens 4 sollte man hinbekommen. Mehr auch
> mit etwas Stack-Pointer-Gymnastik.

Hast du im Ernst schon jemals einen ARM7TDMI aus der Nähe gesehen?
Nö, haste nicht.
Und das verstehende Lesen scheint dir auch nicht sympathisch zu sein.

W.S.

von Dr. Sommer (Gast)


Lesenswert?

W.S. schrieb:
> Hast du im Ernst schon jemals einen ARM7TDMI aus der Nähe gesehen?

Selbst wenn nicht, hätte ich trotzdem Recht. Die "SVC"-Instruktion 
verändert nicht die Inhalte der 13 General-Purpose-Register, welche 
man somit zur Parameter-Übergabe nutzen kann.

Eine Int2Hex-Funktion ist eine allgemeine datenverarbeitende 
Hilfsfunktion und gehört nicht in einen Syscall. Alles was als 
Userspace-Funktion implementiert werden kann, sollte das auch, ggf. in 
einer Library. Dies trennt die OS-Teile vom Anwendungsteil. Du kannst 
deinen Funktionen anscheinend nichtmal übergeben, auf welchen 
Dateideskriptor oder Hardware die Ausgabe erfolgen soll; du hast hier 
die völlig unabhängigen Dinge "V24-Ausgabe" und "Int2Hex" miteinander 
verknüpft, sodass man sie nicht mehr getrennt nutzen kann. Ganz 
schlechtes Design. Linux macht vor wie das richtig geht: Ein 
"write"-Syscall welcher einen beliebigen Puffer ausgibt, und komfortable 
Formatierungsfunktionen wie printf in der C-Library. Da Syscalls ein 
extra Overhead sind (insbesondere auf Systemen mit virtuellem 
Speicher/MMU) sollte man möglichst viele Daten mit einem Aufruf 
übergeben, und nicht jeden Integer einzeln.

W.S. schrieb:
> Und das verstehende Lesen scheint dir auch nicht sympathisch zu sein.

Du bist doch der, welcher trotz wiederholter Versuche nichts dazulernt. 
Wie war das mit dem mehrdimensionalen Array?

von W.S. (Gast)


Lesenswert?

Dr. Sommer schrieb:
> Selbst wenn nicht, hätte ich trotzdem Recht. Die "SVC"-Instruktion
> verändert nicht die Inhalte der 13 General-Purpose-Register, welche
> man somit zur Parameter-Übergabe nutzen kann.

Aha, du kennst also auch nichts von sowas wie einem ABI, also den 
Aufruf-Konventionen für die betreffende Plattform.

W.S.

von Dr. Sommer (Gast)


Lesenswert?

W.S. schrieb:
> Aha, du kennst also auch nichts von sowas wie einem ABI, also den
> Aufruf-Konventionen für die betreffende Plattform.

Der AAPCS, das ABI für ARM, benutzt 4 Register r0-r3 für die Parameter 
Übergabe. Also 4 32bit-Words. Mehr Words kann man auf dem Stack 
übergeben. Also, warum nochmal kannst du nur ein Word per SVC übergeben?

von Dr. Sommer (Gast)


Lesenswert?

PS: Und warum muss man sich bei SVC an das normale ABI halten? Selbst 
Linux nutzt hier mehr als 4 Register für mehr Parameter.

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Dr. Sommer schrieb:
> Warum benutzt du auf ARM die
> unsägliche Windows-Nomenklatur mit word=16bit, wo ARM das schon ewig als
> 32bit festlegt?

Ach Gottchen. Dann ersetzt man eben word durch uint16_t und long durch 
uint32_t usw. Dafür sind die types ja da. Die Methode von W.S. 
jedenfalls benutze ich auch seit Jahren ohne Problem auf STM32, AVR, 
8051, PIC usw. Nechteilig finde ich nur, das die Tabelle auf manchen 
Plattformen im RAM liegen muss. Aber wer heute keine 16 Bytes mehr frei 
hat, der hat auch noch andere Probleme.

von Dr. Sommer (Gast)


Lesenswert?

Matthias S. schrieb:
> Ach Gottchen. Dann ersetzt man eben word durch uint16_t und long durch
> uint32_t usw. Dafür sind die types ja da.

Sollte man halt auch machen anstatt dieses Pascal-in-C zu verbreiten...

von Uhu U. (uhu)


Lesenswert?

So kann mans auch machen:
1
char *uintToHexStr(unsigned num, char* buff, size_t len) {
2
    if (len <= 2)
3
        return NULL;
4
        
5
    buff[--len] = 0;
6
    
7
    if (num == 0) {
8
        buff[--len] = '0';
9
        return buff + len;
10
    }
11
    
12
    while (num != 0 && len >= 1) {
13
        buff[--len] = '0' + (num & 0x0f);
14
        if (buff[len] > '9')
15
            buff[len] += 7;
16
        num >>= 4;
17
    }
18
    
19
    if (num != 0)
20
        return NULL;
21
    
22
    return buff + len;
23
}
der 3. Parameter ist die Länge des Puffers in char.

Die Funktion gibt einen Zeiger auf den Ergebnis-Puffer zurück, wenn die 
Zahl gewandelt werden konnte und NULL, wenn der Puffer für die Wandlung 
zu kurz war.

: Bearbeitet durch User
von zitter_ned_aso (Gast)


Lesenswert?

und warum lässt ihr immer das Präfix "0x" weg?

von Dirk B. (dirkb2)


Lesenswert?

zitter_ned_aso schrieb:
> und warum lässt ihr immer das Präfix "0x" weg?

weil das nicht immer nötig ist.
weil das nur eine Notation von C ist (es gibt auch andere)
weil man es auch einfach davor extra ausgeben kann

von Zeno (Gast)


Lesenswert?

Dirk B. schrieb:
> zitter_ned_aso schrieb:
>> und warum lässt ihr immer das Präfix "0x" weg?
>
> weil das nicht immer nötig ist.
> weil das nur eine Notation von C ist (es gibt auch andere)
> weil man es auch einfach davor extra ausgeben kann

Das hat überhaupt nichts mit Notation zu tun. mit 0x werden hexadezimale 
Zahlen gekennzeichnet. Da kann man auch nichts extra ausgeben.
Da in den obigen Beispielen nur Zahlen von 0..9 benutzt werden muß es 
nicht hexadezimal sein, da bekanntlich bis zur Ziffer 9 dezimal und 
hexadezimal   identisch sind.

Allerdings gehört es nach meinem Verständnis zum guten 
(Programmier)Stil, das man es in einem Quelltext einheitlich macht. An 
Stelle von 0x0f hätte Uhu dann auch 15 schreiben sollen.

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Zeno schrieb:
> An
> Stelle von 0x0f hätte Uhu dann auch 15 schreiben sollen.

Das macht m.E. aber die Maskierung des Nibbles deutlicher, als wenn du 
15 schreibst.

von Zeno (Gast)


Lesenswert?

Matthias S. schrieb:
> Zeno schrieb:
>> An
>> Stelle von 0x0f hätte Uhu dann auch 15 schreiben sollen.
>
> Das macht m.E. aber die Maskierung des Nibbles deutlicher, als wenn du
> 15 schreibst.

Ich wollte eigentlich nur ausdrücken, das ich es in einem Quelltext 
einheitlich machen würde.
Also alles wo es um Bit oder Byteoperationen geht würde ich dann 
einheitlich als Hex schreiben alles andere im gewohnten Dezimalsystem.

Letztendlich ist das aber völlig Rille, es soll jeder so schreiben wie 
es ihm angenehm ist und da ist es eben wurscht ob ich 0xff oder 255 oder 
0377 oder 0b11111111 schreibe. Es ist immer die gleiche Zahl, die Frage 
ist halt nur was am Ende lesbarer ist.

von Gurgl (Gast)


Lesenswert?

Zeno schrieb:
> Da in den obigen Beispielen nur Zahlen von 0..9 benutzt werden muß es
> nicht hexadezimal sein, da bekanntlich bis zur Ziffer 9 dezimal und
> hexadezimal   identisch sind.

Dachte ich auch beim ersten Hingucken aber hinterher kommt ein +7 damit 
springt er von der 9 zum A ;)

von Zeno (Gast)


Lesenswert?

Gurgl schrieb:
> Dachte ich auch beim ersten Hingucken aber hinterher kommt ein +7 damit
> springt er von der 9 zum A ;)

Das A kommt aber in dem Quelltext nicht "optisch" vor, soll heisen man 
sieht es nicht im Quelltext.

Sein Code hat aber einen kleinen Fehler. Die Zeile if (len <= 2) muß 
richtigerweise if (len <= 1) heißen. Ansonsten wird bei einem zu groß 
gewählten Puffer ein Leerstring ausgegeben. Beispiel mit Puffergröße 3:

num=15 müßte 'F' ergeben ergibt aber Leerstring
num=16 ergibt korrekt '10'

Je größer Puffer wird um so extremer wird das Verhalten

von Zeno (Gast)


Lesenswert?

Grad noch mal getestet, es geht trotzdem nicht. Die 2 sind schon korrekt 
wegen dem '\0' Zeichen.
Man muß den Puffer vorher mit dem Zeichen '0' initialisieren, wenn man 
ein halbwegs brauchbares Ergebnis haben will.

von Uhu U. (uhu)


Lesenswert?

Matthias S. schrieb:
> Zeno schrieb:
>> An
>> Stelle von 0x0f hätte Uhu dann auch 15 schreiben sollen.
>
> Das macht m.E. aber die Maskierung des Nibbles deutlicher, als wenn du
> 15 schreibst.

So ist es…

von Uhu U. (uhu)


Lesenswert?

Zeno schrieb:
> Das A kommt aber in dem Quelltext nicht "optisch" vor, soll heisen man
> sieht es nicht im Quelltext.

Als erfahrener Programmierer weiß man, dass ASCII '9' + 7 == 'A' ist…

Das ist ein uralter Trick aus den Zeiten, in denen noch fast alles in 
ASM programmiert wurde.

von Uhu U. (uhu)


Lesenswert?

Zeno schrieb:
> Grad noch mal getestet, es geht trotzdem nicht.

Da hast du wohl einen kleinen Fehler gemacht:
Ergebnis ist nicht die Variable buff, sondern der Pointer, den 
uintToHexStr zurück gibt. Der zeigt je nach Länge von Buff und Größe der 
Zahl irgendwo in buff hinein. Was davor steht, interessiert nicht.

Die Routine hat zudem noch einen Vorteil: sie schreibt nichts kaputt, 
wenn der Puffer zu klein ist.

Nebenbei:
   Nach demselben Prinzip kann man eine Zahl auch ins Dezimalsystem
   wandeln. Statt num & 0x0f muss man dann eben num % 10 schreiben und
   statt num >>= 4; num /= 10;. Der Trick mit dem + 7 kann dann
   natürlich entfallen.

Die Anweisung
1
        if (buff[len] > '9')
2
            buff[len] += 7;
kann man natürlich auch so schreiben:
1
        if (buff[len] > '9')
2
            buff[len] += 'A' - '9';
und wenn man statt 'A' 'a' schreibt, kommt das Ergebnis in 
Kleinbuchstaben…

Diese Schreibweise hat den Vorteil, dass der Code auch in anderen 
(gängigen) Zeichencodierungen funktioniert. Den Ausdruck 'A' - '9' 
berechnet der Compiler und setzt dafür die Konstante 7 ein.

: Bearbeitet durch User
von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Uhu U. schrieb:
> Den Ausdruck 'A' - '9' berechnet der Compiler und setzt dafür
> die Konstante 7 ein.

Vorteil ist vor allem die bessere Lesbarkeit.  Und man kann es auch in 
Assembler verwenden, die unterstützen solche einfachen Ausdrücke auch.

von PIClig (Gast)


Lesenswert?

> template <typename T>
> std::size_t leadingZeroes (T x) {
>   std::size_t c = 0;
>   while ((x & (T { 1 } << (std::numeric_limits<T>::digits - 1))) == 0) { x > <<= 
1; ++c; }
> return c;
> }

Wenn ich diesen CPP-Murx sehe, wird mir schlecht.
Das verbraucht ja selbet im hässlichem PIC-Assembler weniger Zeichen
und ist aussagekräftiger.

von Michael B. (laberkopp)


Lesenswert?

Dr. Sommer schrieb:
> Für die Lästernden, hier eine Version welche von links an ausgibt und
> daher ohne Barrel-Shifter ineffizient ist:

Uhu U. schrieb:
> So kann mans auch machen:
1
char *hexout(char *buf,unsigned x)
2
{
3
    if(x>0xF) buf=hexout(buf,x>>4);
4
    x&=0xF;
5
    *buf++=(x>9?'A'-10:'0')+x;
6
    return buf;
7
}
bzw.
1
hexout(unsigned x)
2
{
3
    if(x>0xF) hexout(x>>4);
4
    x&=0xF;
5
    putc((x>9?'A'-10:'0')+x);
6
}

Beitrag #5838675 wurde vom Autor gelöscht.
von Dr. Sommer (Gast)


Lesenswert?

PIClig schrieb:
> Wenn ich diesen CPP-Murx sehe, wird mir schlecht.

Ja, der C PräProzessor ist schlimm. Das ist aber C++.

PIClig schrieb:
> Das verbraucht ja selbet im hässlichem PIC-Assembler weniger Zeichen
> und ist aussagekräftiger.

Auch für sämtlich Integer-Typen und Plattformen? Wohl kaum.

Michael B. schrieb:
>> So kann mans auch machen:

Da das nicht Tail-Rekursiv ist - können Optimizer das iterativ umbauen?

von Uhu U. (uhu)


Lesenswert?

Dr. Sommer schrieb:
> Da das nicht Tail-Rekursiv ist - können Optimizer das iterativ umbauen?

Vermutlich nicht, aber kann sowas ja auch iterativ programmieren… Man 
muss eben ein f von links nach rechts drüber schieben und mit & die 
betreffende Tetrade heraus picken, die man dann entsprechend nach rechts 
schiebt…

von Dr. Sommer (Gast)


Lesenswert?

Uhu U. schrieb:
> Man
> muss eben ein f von links nach rechts drüber schieben und mit & die
> betreffende Tetrade heraus picken, die man dann entsprechend nach rechts
> schiebt…

Dann muss man aber doppelt shiften - sowohl das "f", und jedes mal das 
gewünschte Nibble nach Rechts. Ohne Barrelshifter eher blöd. Besser ist 
es, den Wert selbst kontinuierlich weiter zu shiften.

von Uhu U. (uhu)


Lesenswert?

Dr. Sommer schrieb:
> Dann muss man aber doppelt shiften - sowohl das "f", und jedes mal das
> gewünschte Nibble nach Rechts. Ohne Barrelshifter eher blöd.

Wir sind hier im "Forum: PC-Programmierung" - PC-Prozessoren haben 
Barrelshifter…

von Zeno (Gast)


Lesenswert?

Uhu U. schrieb:
> Als erfahrener Programmierer weiß man, dass ASCII '9' + 7 == 'A'

Das habe ich ja nicht bezweifelt das dem so ist.

Uhu U. schrieb:
> Zeno schrieb:
>> Grad noch mal getestet, es geht trotzdem nicht.
>
> Da hast du wohl einen kleinen Fehler gemacht:

Stimmt! Ich hatte mir nur den Puffer angeschaut und nicht das Ergebnis 
der Funktion.
Sorry war mein Fehler.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.