Hello everyone,
How do I convert a decimal to a hexadecimal in code? I'm making an RGB color picker and I want to display the RGB value and the hex value of the color.
Hello everyone,
How do I convert a decimal to a hexadecimal in code? I'm making an RGB color picker and I want to display the RGB value and the hex value of the color.
Serial.print(15, HEX); will print F
Why does it matter?
The computer doesn't care about binary, octal, decimal or hexadecimal
This is a very old problem, for which many new people don't know the answer.
Hexadecimal is a base 16 number system.
So when you divide a decimal number by 16, the remainder is one hexadecimal digit.
Then translate the remained into a character.
remainder of 0 goes into the character "0" ... the remainder of 15 goes into the character "F".
Take the quotient of the division,
and preform the same process on the remainder. So people write a recursive function to
do this. or you can make it a function that returns the quotient, and check for a zero value
in a while loop.
Hope this helps
just_a_guy:
This is a very old problem, for which many new people don't know the answer.Hexadecimal is a base 16 number system.
So when you divide a decimal number by 16, the remainder is one hexadecimal digit.
Then translate the remained into a character.
remainder of 0 goes into the character "0" ... the remainder of 15 goes into the character "F".
Take the quotient of the division,
and preform the same process on the remainder. So people write a recursive function to
do this. or you can make it a function that returns the quotient, and check for a zero value
in a while loop.Hope this helps
That’s the theory on a piece of paper...
But no one (well may be you do?) does it like this in a typical computer because division and modulo are costly operations...
In practice data in your typical computer is stored in binary (base 2 - with 0 and 1) and accessed in bytes. As 24 = 16, if you read 4 bits in one go you can have an HEX digit right there (base 16) —> So we would just iterate through the content of those bytes - first the 4 High bits and then 4 Low bits (with masking/shifting) - and use that to build the HEX representation
decimal to hexadecimal? -
google for- decimal to hexadecimal online converter
cwduffy01:
...I want to display the RGB value and the hex value of the color.
you can build a string using C Format Specifiers
void setup()
{
Serial.begin(9600);
uint8_t red = 255;
uint8_t green = 125;
uint8_t blue = 0;
char colorBuffer[24];
snprintf(colorBuffer, sizeof(colorBuffer), "R:0x%02X G:0x%02X B:0x%02X", red, green, blue);
Serial.println(colorBuffer);
}
void loop()
{
}
Here's a sample program that uses just_a_guy's suggestion of recursive function:
// Recursive function that prints a number out in HexaDecimal ASCII
void printHex(unsigned int numberToPrint)
{
if (numberToPrint >= 16)
printHex(numberToPrint / 16);
Serial.print("0123456789ABCDEF"[numberToPrint % 16]);
}
void setup() {
Serial.begin(9600);
printHex(1234); // Output => 4D2
Serial.println();
printHex(0x1234); // Output => 1234
Serial.println();
}
void loop() {
}
J-M-L is correct, that arbitrary division and mod operations are costly, however, dividing by 16 and mod 16 can be done with more efficient bit shifts and logical AND instructions respectively. Luckily, the compiler is smart enough to recognize this and created impressively efficient assembly code.
void byte_to_str(char* buff, uint8_t val) { // convert an 8-bit byte to a string of 2 hexadecimal characters
buff[0] = nibble_to_hex(val >> 4);
buff[1] = nibble_to_hex(val);
}
char nibble_to_hex(uint8_t nibble) { // convert a 4-bit nibble to a hexadecimal character
nibble &= 0xF;
return nibble > 9 ? nibble - 10 + 'A' : nibble + '0';
}
Usage:
uint8_t red = 16; // 16 = 0x10
uint8_t green = 0; // 0 = 0x00
uint8_t blue = 255; // 255 = 0xFF
// as a char array
char rgbTxt[7];
rgbTxt[0] = '#';
byte_to_str(&rgbTxt[1], red);
byte_to_str(&rgbTxt[3], green);
byte_to_str(&rgbTxt[5], blue);
Serial.write(rgbTxt, 7); // prints '#1000FF'
// as a null-terminated char array (string)
char rgbTxt[8];
rgbTxt[0] = '#';
byte_to_str(&rgbTxt[1], red);
byte_to_str(&rgbTxt[3], green);
byte_to_str(&rgbTxt[5], blue);
rgbTxt[7] = '\0';
Serial.print(rgbTxt); // prints '#1000FF'
Alternatively (uses much more memory):
char rgbTxt[8];
sprintf(rgbTxt, "#%02X%02X%02X", red, green, blue);
Serial.write(rgbTxt, 7); // or
Serial.print(rgbTxt);
Or (not supported on AVR):
Serial.printf("#%02X%02X%02X", red, green, blue);
Pieter
I'm curious - how does return nibble > 9 ? nibble - 10 + 'A' : nibble + '0';compare to return "0123456789ABCDEF"[nibble]; for code size?
Very interesting ... The one with the string indexing uses 16 bytes of (static) RAM more than the one with the ternary operator. What surprises me is that it uses 14 bytes of flash more.
My intuition says that the first one should require more operations.
Which one would be fastest? The first one does have branching ...
char nibble_to_hex(uint8_t nibble) { // convert a 4-bit nibble to a hexadecimal character (31684 B)
nibble &= 0xF;
return nibble > 9 ? nibble - 10 + 'A' : nibble + '0'; // 488 B flash, 12 B RAM
// return "0123456789ABCDEF"[nibble]; // 502 B flash, 28 B RAM
}
volatile uint8_t value = 12;
volatile char hex;
void setup() {
hex = nibble_to_hex(value);
}
void loop() {}
I'm fairly sure the string could remain in flash with a PSTR.
@PieterP The way I understand it, any initializers you use for variables in RAM have to be initialized in Flash and copied there, so yeah the 17 bytes have to be there in Flash as well.
@AWOL I tried to figure out how to pull the data straight out of flash without declaring it in RAM first. Turns out it's not that simple. Not like simply adding an F() macro.
I got it to work like this:
const char hexCharArray[] PROGMEM = "0123456789ABCDEF";
void printHex(unsigned int numberToPrint)
{
if (numberToPrint >= 16)
printHex(numberToPrint / 16);
Serial.print(pgm_read_byte(hexCharArray + numberToPrint % 16));
}
However, it uses 1740 bytes of Flash, and 188 bytes of RAM.
This one uses 1550 of Flash, and 204 of RAM:
const char hexCharArray[] = "0123456789ABCDEF";
void printHex(unsigned int numberToPrint)
{
if (numberToPrint >= 16)
printHex(numberToPrint / 16);
Serial.print(hexCharArray[numberToPrint % 16]);
}
So yeah, I saved 16 bytes of RAM, at the expense of 180 bytes of Flash?!?
I looked at the assembly code it produced, and it generates in-line code to pull the data directly from flash. It doesn't take any 180 bytes to do that. So I'm guessing that using pgm_read_byte() pulls in code from a library or object file that doesn't show up in my code anywhere. And probably never gets used.
I tried to find the source code where it does that stuff, and it's hidden pretty well. Of course doing a grep "F" * is useless. And PSTR doesn't seem to have any references in the documentation.
Still looking...
Nervermind. I found it. Somehow typecasting it makes it work. Flash 1554, RAM 188:
const char hexCharArray[] PROGMEM = "0123456789ABCDEF";
void printHex(unsigned int numberToPrint)
{
if (numberToPrint >= 16)
printHex(numberToPrint / 16);
Serial.print((char)pgm_read_byte(hexCharArray + numberToPrint % 16));
}