-1

I'm having problems converting negative numbers, from decimal base to hexadecimal base, with the following function:

#include <stdio.h>
 
int main()
{
    int quotient, remainder;
    int i, j = 0;
    char hexadecimalnum[100];
 
    quotient = -50;
 
    while (quotient != 0)
    {
        remainder = quotient % 16;
        if (remainder < 10)
            hexadecimalnum[j++] = 48 + remainder;
        else
            hexadecimalnum[j++] = 55 + remainder;
        quotient = quotient / 16;
    }

    strrev(hexadecimalnum);

    printf("%s", hexadecimalnum);
    return 0;
}

For quotient = -50; the correct output should be:

ffffffce

But this function's output is:

.

With positive numbers the output is always correct but with negative numbers not.

I'm having a hard time understanding to why it doesn't work with negative numbers.

  • 1
    Your question is a little ill-defined. If you want a hexadecimal representation of the *internal two's complement representation* of a negative number, you're going to have to, at minimum, use `unsigned int` instead of `int`. – Steve Summit May 06 '22 at 11:21
  • I'm surprised you say "With positive numbers the output is always correct", because when I try it on a few numbers, the digits look backwards. – Steve Summit May 06 '22 at 11:25
  • 1
    You're also not properly null-terminating the string you construct in `hexadecimalnum`. – Steve Summit May 06 '22 at 11:26
  • Works great for 85, 119, 170, and 257, though. :-) – Steve Summit May 06 '22 at 11:30
  • 2
    Don't use magic numbers. Use `'A' + remainder - 10` so that your code turns readable. – Lundin May 06 '22 at 11:34
  • Thanks @Steve Summit, you actually helped a lot. I'm beginning and int vs unsigned int is still not very present. – Mário Lima May 06 '22 at 11:34
  • 3
    Have you learned (or at least tested) what the remainder is in `remainder = quotient % 16;` for negative `quotient`...? – CiaPan May 06 '22 at 11:38
  • 1
    Strictly speaking, a number represented in decimal as `-50` should in hexadecimal look like `-32`. The value `ffffffce` you present as an expected output is, as @SteveSummit noted, a hexadecimal dump of a two's complement representation of the negative number. This, however, depends on the size of your `int`, for example in 16-bit architecture it could be half that long: `ffce`. And if you actually want that, you should rather - for the sake of consistency - represent `-50` as `999950` or sth.... – CiaPan May 06 '22 at 11:47

2 Answers2

0

You can use printf's format specifier "%08x", then you can print any number in their respective hexadecimal representation.

#include <stdio.h>

void num_to_hex(int a, char *ptr) { snprintf(ptr, 9, "%08x", a); }

int main() {
    char hex[10] = {};
    num_to_hex(-50, hex);
    printf("%s\n", hex);
    return 0;
}

Output:

ffffffce
Darth-CodeX
  • 2,166
  • 1
  • 6
  • 23
  • Thanks for your answer but unfortunately I need a function that replicates that same thing. – Mário Lima May 06 '22 at 11:27
  • 3
    Implementing these conversions manually makes perfect sense in applications where `*printf` is banned for the reason that it's slow, dangerous crap function. Real-time systems, safety-related systems, fast executing systems... The OP's code is likely some 10 to 100 times more efficient and it's not even optimized well. – Lundin May 06 '22 at 11:40
  • 1
    For those just joining: `printf` and friends are wonderful functions. Don't let Lundin's opinionated rants scare you away from it. And his implication that it's necessarily orders of magnitude slower is an exaggeration for dramatic effect. – Steve Summit May 06 '22 at 12:01
  • @SteveSummit I take it you have never tried to squeeze in `sprintf` into a microcontroller for the purpose of integer to string conversions? I've benchmarked and disassembled such examples many times, every time some PC programmer stumbles in and starting to use `sprintf`. Rough numbers for a low-end generic MCU are: manual integer-to-string conversion: some 80-100 bytes code = some 30 instructions. Using `sprintf`: some 1000-2000 bytes of code = some 700 instructions. Meaning it takes around 10 times more space and executes some 30 times longer _for no gain what-so-ever_. – Lundin May 06 '22 at 12:35
  • @Lundin You aren't understanding that OP hasn't said he's on a micro-controller. – Darth-CodeX May 06 '22 at 12:39
  • @Darth-CodeX You aren't understanding that performance should matter everywhere. "I'm a PC programmer so it's fine to me to write slop" isn't an acceptable stance at least not for me. Sure, rolling out your own console printing function on a PC is a big task and not recommended. But doing trivial string/integer conversions manually is not hard. – Lundin May 06 '22 at 12:42
  • Also the biggest argument against printf/scanf is that they are possibly _the_ most bug-prone functions ever designed in the history of programming. Surely they are the most expensive functions ever designed in terms of monetary damage done to mankind. The non-existent type safety, the long list of UB, the variadic interface... I can go on, but in order to list all design problems with stdio.h I would have to write a thick book on the topic. – Lundin May 06 '22 at 12:45
  • @SteveSummit Actually the previous numbers were _far_ too generous. Taking the fixed code I posted here (which got some notable performance problems and can be improved) and comparing it with a single sprintf call, then by the time the manual code is complete, the expensive sprintf call has not even been carried out. I picked some mainstream slow 8-bitter as example. The former gives 40 instructions for the _whole code_ while the sprintf version gives 50 instructions as mere instruction call overhead, not counting the actual heavy function. https://godbolt.org/z/GP8e8Wzrx – Lundin May 06 '22 at 13:00
  • 1
    @Lundin Just a few points, although obviously neither of us is going to change the other's mind. (1) *You aren't understanding that performance should matter everywhere* In which case it's quite irresponsible of you to be using C at all; you should obviously be using assembly everywhere. (2) *non-existent type safety* That's because originally, C never had type-checking on any function arguments. (3) Comparing an inline int-to-string conversion against *printf is an apples-to-oranges comparison. The interesting comparison would be an optimized `sprintf` versus an optimized `itoa`. – Steve Summit May 06 '22 at 14:29
0

Some fixes:

  • unsigned int quotient - you need to convert -50 to a large hex number in two's complement or you'll get the wrong number of iterations (2) in the loop, instead of 8 as required.
  • Removal of "magic numbers": '0' + remainder and 'A' + remainder - 10.
  • Zero initialize hexadecimalnum becaues it needs to be null terminated before printing a string from there. Better yet, add the null termination explicitly.
  • Use for loops when possible.
  • Might as well store the characters from the back to front and save the extra call of reversing the string.

Result:

#include <stdio.h>

// 4 bytes*2 = 8 nibbles
#define HEX_STRLEN (sizeof(int)*2) 
 
int main()
{
    unsigned int remainder;
    int i = 0;
    char hex[100];
 
    for(unsigned int q = -50; q!=0; q/=16)
    {
      remainder = q % 16;
      if (remainder < 10)
        hex[HEX_STRLEN-i-1] = '0' + remainder;
      else
        hex[HEX_STRLEN-i-1] = 'A' + remainder - 10;
      i++;
    }
    hex[HEX_STRLEN] = '\0'; // explict null termination
    printf("%s\n", hex);
}

(There's lots of improvements than can be made still, this is just to be considered as the first draft.)

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • You are using `printf()`, consider using `puts()` – Darth-CodeX May 06 '22 at 12:16
  • @Darth-CodeX Sure but not for performance reasons. https://godbolt.org/z/xf1cqxz49 – Lundin May 06 '22 at 12:29
  • 1
    The code looks nice at a first glance, but it doesn't `return` any value even though the `main()` function is defined as returning `int`. It also doesn't seem to work correctly for `q=0` and for any positive values less than 268435456... So, from the code which correctly handles positive numbers but not negative, you made the one which correctly handles negative numbers but no positive. It's a big change but a little progress.... – CiaPan May 06 '22 at 14:49
  • 2
    @CiaPan These days, `main` is special — you get an [implicit `return 0;`](https://stackoverflow.com/questions/13545291/can-i-omit-return-from-main-in-c) if you forget to add an explicit one. – Steve Summit May 06 '22 at 15:48
  • @CiaPan Yes this code has lots of flaws, I was merely pointing out the most obvious bugs. The OP has to take it from here and add all error handling etc. Regarding missing `return 0` from main(), check out the C language news from year 1999. – Lundin May 06 '22 at 17:02
  • I'd edit the answer myself, to change `q!=0` to `i – Steve Summit May 06 '22 at 20:12
  • Yes, I know. However, this is not just an emergency help service to handle "solve _my_ problem" requests, but rather a Question&Answer site, where people can (ideally...) _find_ answers to their questions (and, possibly, solutions to their problems). An answer which solves just a half of the problem actually leaves the problem ...unsolved. – CiaPan May 06 '22 at 20:13