hex and unsigned and signed decimal

Discussion in 'C Programming' started by me2, Dec 28, 2006.

  1. me2

    me2 Guest

    I am writing a little base conversion utility called base.c.

    This is what base does.

    $ base -127
    Signed decimal: -127
    Unsigned decimal: 4294967169
    Hexidecimal: 0xffffff81
    Octal: O37777777601
    Binary: 1098 7654 3210 9876 5432 1098 7654 3210
    1111 1111 1111 1111 1111 1111 1000 0001
    $ base 127
    Signed decimal: 127
    Unsigned decimal: 127
    Hexidecimal: 0x7f
    Octal: O177
    Binary: 1098 7654 3210 9876 5432 1098 7654 3210
    0000 0000 0000 0000 0000 0000 0111 1111

    However, base has a bug when one inputs sign extended hexadecimal or octal
    numbers, like this.

    $ base 0xffffff81
    Signed decimal: 2147483647
    Unsigned decimal: 2147483647
    Hexidecimal: 0x7fffffff
    Octal: O17777777777
    Binary: 1098 7654 3210 9876 5432 1098 7654 3210
    0111 1111 1111 1111 1111 1111 1111 1111

    Base works like this. I use sscanf %i to convert argv[1] to an int. Then
    I cast the int to an unsigned int and do the rest of the manipulation.
    However, as the above example shows, it doesn't work properly when I input
    a sign extended octal or hex number.

    The decimal of 0xffffff81 should be 4294967169, but it isn't. Why ?

    Thanks
    me2, Dec 28, 2006
    #1
    1. Advertising

  2. me2

    Scorpio Guest

    me2 wrote:
    > I am writing a little base conversion utility called base.c.
    >
    > This is what base does.
    >
    > $ base -127
    > Signed decimal: -127
    > Unsigned decimal: 4294967169
    > Hexidecimal: 0xffffff81
    > Octal: O37777777601
    > Binary: 1098 7654 3210 9876 5432 1098 7654 3210
    > 1111 1111 1111 1111 1111 1111 1000 0001
    > $ base 127
    > Signed decimal: 127
    > Unsigned decimal: 127
    > Hexidecimal: 0x7f
    > Octal: O177
    > Binary: 1098 7654 3210 9876 5432 1098 7654 3210
    > 0000 0000 0000 0000 0000 0000 0111 1111
    >
    > However, base has a bug when one inputs sign extended hexadecimal or octal
    > numbers, like this.
    >
    > $ base 0xffffff81
    > Signed decimal: 2147483647
    > Unsigned decimal: 2147483647
    > Hexidecimal: 0x7fffffff
    > Octal: O17777777777
    > Binary: 1098 7654 3210 9876 5432 1098 7654 3210
    > 0111 1111 1111 1111 1111 1111 1111 1111
    >
    > Base works like this. I use sscanf %i to convert argv[1] to an int. Then
    > I cast the int to an unsigned int and do the rest of the manipulation.
    > However, as the above example shows, it doesn't work properly when I input
    > a sign extended octal or hex number.
    >
    > The decimal of 0xffffff81 should be 4294967169, but it isn't. Why ?


    Because %i is only for integers. You will need to check the string in
    argv[1] before scanning it using sscanf. If it is preceded with "0x"
    then use %x (hexadecimal), if its preceded with just "0" then use %o
    (octal) for scanning with sprintf, and later you can convert it to
    other formats.
    Scorpio, Dec 28, 2006
    #2
    1. Advertising

  3. me2

    Scorpio Guest

    Scorpio wrote:
    > me2 wrote:
    > > I am writing a little base conversion utility called base.c.
    > >
    > > This is what base does.
    > >
    > > $ base -127
    > > Signed decimal: -127
    > > Unsigned decimal: 4294967169
    > > Hexidecimal: 0xffffff81
    > > Octal: O37777777601
    > > Binary: 1098 7654 3210 9876 5432 1098 7654 3210
    > > 1111 1111 1111 1111 1111 1111 1000 0001
    > > $ base 127
    > > Signed decimal: 127
    > > Unsigned decimal: 127
    > > Hexidecimal: 0x7f
    > > Octal: O177
    > > Binary: 1098 7654 3210 9876 5432 1098 7654 3210
    > > 0000 0000 0000 0000 0000 0000 0111 1111
    > >
    > > However, base has a bug when one inputs sign extended hexadecimal or octal
    > > numbers, like this.
    > >
    > > $ base 0xffffff81
    > > Signed decimal: 2147483647
    > > Unsigned decimal: 2147483647
    > > Hexidecimal: 0x7fffffff
    > > Octal: O17777777777
    > > Binary: 1098 7654 3210 9876 5432 1098 7654 3210
    > > 0111 1111 1111 1111 1111 1111 1111 1111
    > >
    > > Base works like this. I use sscanf %i to convert argv[1] to an int. Then
    > > I cast the int to an unsigned int and do the rest of the manipulation.
    > > However, as the above example shows, it doesn't work properly when I input
    > > a sign extended octal or hex number.
    > >
    > > The decimal of 0xffffff81 should be 4294967169, but it isn't. Why ?

    >
    > Because %i is only for integers. You will need to check the string in
    > argv[1] before scanning it using sscanf. If it is preceded with "0x"
    > then use %x (hexadecimal), if its preceded with just "0" then use %o
    > (octal) for scanning with sprintf, and later you can convert it to
    > other formats.


    Correction...I meant scanning with sscanf, not sprintf. Sorry.
    Scorpio, Dec 28, 2006
    #3
  4. me2

    Scorpio Guest

    Scorpio wrote:
    > Scorpio wrote:
    > > me2 wrote:
    > > > I am writing a little base conversion utility called base.c.
    > > >
    > > > This is what base does.
    > > >
    > > > $ base -127
    > > > Signed decimal: -127
    > > > Unsigned decimal: 4294967169
    > > > Hexidecimal: 0xffffff81
    > > > Octal: O37777777601
    > > > Binary: 1098 7654 3210 9876 5432 1098 7654 3210
    > > > 1111 1111 1111 1111 1111 1111 1000 0001
    > > > $ base 127
    > > > Signed decimal: 127
    > > > Unsigned decimal: 127
    > > > Hexidecimal: 0x7f
    > > > Octal: O177
    > > > Binary: 1098 7654 3210 9876 5432 1098 7654 3210
    > > > 0000 0000 0000 0000 0000 0000 0111 1111
    > > >
    > > > However, base has a bug when one inputs sign extended hexadecimal or octal
    > > > numbers, like this.
    > > >
    > > > $ base 0xffffff81
    > > > Signed decimal: 2147483647
    > > > Unsigned decimal: 2147483647
    > > > Hexidecimal: 0x7fffffff
    > > > Octal: O17777777777
    > > > Binary: 1098 7654 3210 9876 5432 1098 7654 3210
    > > > 0111 1111 1111 1111 1111 1111 1111 1111
    > > >
    > > > Base works like this. I use sscanf %i to convert argv[1] to an int. Then
    > > > I cast the int to an unsigned int and do the rest of the manipulation.
    > > > However, as the above example shows, it doesn't work properly when I input
    > > > a sign extended octal or hex number.
    > > >
    > > > The decimal of 0xffffff81 should be 4294967169, but it isn't. Why ?

    > >
    > > Because %i is only for integers. You will need to check the string in
    > > argv[1] before scanning it using sscanf. If it is preceded with "0x"
    > > then use %x (hexadecimal), if its preceded with just "0" then use %o
    > > (octal) for scanning with sprintf, and later you can convert it to
    > > other formats.

    >
    > Correction...I meant scanning with sscanf, not sprintf. Sorry.


    More correction. I just realized that %x and %o are only for printing,
    and its cannot be used for scanning. So you'll have to find some other
    method to read hexadecimal and octal values.
    Scorpio, Dec 28, 2006
    #4
  5. me2

    CBFalconer Guest

    me2 wrote:
    >

    .... snip ...
    >
    > However, base has a bug when one inputs sign extended hexadecimal
    > or octal numbers, like this.
    >
    > $ base 0xffffff81
    > Signed decimal: 2147483647
    > Unsigned decimal: 2147483647
    > Hexidecimal: 0x7fffffff
    > Octal: O17777777777
    > Binary: 1098 7654 3210 9876 5432 1098 7654 3210
    > 0111 1111 1111 1111 1111 1111 1111 1111
    >
    > Base works like this. I use sscanf %i to convert argv[1] to an
    > int. Then I cast the int to an unsigned int and do the rest of
    > the manipulation. However, as the above example shows, it doesn't
    > work properly when I input a sign extended octal or hex number.
    >
    > The decimal of 0xffffff81 should be 4294967169, but it isn't.
    > Why ?


    You have experienced integer overflow, after which behaviour is
    undefined. Therefore the answers you are getting (or anything
    else) are perfectly valid.

    --
    Merry Christmas, Happy Hanukah, Happy New Year
    Joyeux Noel, Bonne Annee.
    Chuck F (cbfalconer at maineline dot net)
    <http://cbfalconer.home.att.net>
    CBFalconer, Dec 28, 2006
    #5
  6. me2

    me2 Guest

    On Thu, 28 Dec 2006 07:04:32 -0800, Scorpio wrote:

    > Because %i is only for integers. You will need to check the string in
    > argv[1] before scanning it using sscanf. If it is preceded with "0x"
    > then use %x (hexadecimal), if its preceded with just "0" then use %o
    > (octal) for scanning with sprintf, and later you can convert it to
    > other formats.


    But man sscanf says this:

    The following conversion specifiers are available:
    ....

    i Matches an optionally signed integer; the next pointer must be a
    pointer to int. The integer is read in base 16 if it
    begins with 0x or 0X, in base 8 if it begins with 0, and
    in base 10 otherwise. Only characters that correspond to
    the base are used.

    So why isn't it properly reading the hex and octal numbers ?

    Thanks.
    me2, Dec 28, 2006
    #6
  7. me2 <> writes:

    > I am writing a little base conversion utility called base.c.
    >
    > This is what base does.

    [cut]
    > However, base has a bug when one inputs sign extended hexadecimal or octal
    > numbers, like this.

    [cut]
    > I use sscanf %i to convert argv[1] to an int.


    You'd better use strtol() for that which can automatically read
    decimal, octal or hexadecimal numbers and provides better error
    checking I guess. You could even use strtoll() for bigger numbers if
    it's available on your system (I believe it's defined in C99).

    #v+
    #include <stdio.h>
    #include <stdlib.o>
    #include <errno.h>

    int main(int argc, char **argv) {
    char *end;
    long long num;

    if (argc!=2) {
    fputs("usage: base <number>\n", stderr);
    return EXIT_FAILURE;
    }

    num = strtoll(argv[1], &end, 0);
    if (*end || errno) {
    fprintf(stder, "%s: invalid number\n", argv[1]);
    return EXIT_FAILURE;
    }

    printf("signed : %lld\n", num);
    printf("unsigned: %llu\n", num);
    printf("hex : %llo\n", num);
    printf("octal : %llx\n", num);
    return EXIT_SUCESS;
    }
    #v-

    (code not tested)


    --
    Best regards, _ _
    .o. | Liege of Serenly Enlightened Majesty of o' \,=./ `o
    ..o | Computer Science, Michal "mina86" Nazarewicz (o o)
    ooo +--<mina86*tlen.pl>---<jid:mina86*chrome.pl>--ooO--(_)--Ooo--
    Michal Nazarewicz, Dec 29, 2006
    #7
  8. Michal Nazarewicz writes:
    > You'd better use strtol() for that which can automatically read
    > decimal, octal or hexadecimal numbers and provides better error
    > checking I guess. You could even use strtoll() for bigger numbers if
    > it's available on your system (I believe it's defined in C99).


    Or lacking long long, handle the sign specially and read the rest into
    an unsigned int with strtoul or %u. Then maybe convert to int - taking
    care to avoid overflow, including for INT_MIN.

    --
    Hallvard
    Hallvard B Furuseth, Dec 29, 2006
    #8
  9. me2

    me2 Guest

    On Thu, 28 Dec 2006 11:14:06 -0500, CBFalconer wrote:


    > You have experienced integer overflow, after which behaviour is
    > undefined. Therefore the answers you are getting (or anything
    > else) are perfectly valid.


    0xffffff81 is a 32 bit number. How can it be integer overflow ? Are you
    saying it will interpret it as an unsigned integer and then try to stuff
    it in a signed int variable and overflow that way ?
    me2, Dec 29, 2006
    #9
  10. me2

    me2 Guest

    On Fri, 29 Dec 2006 11:16:00 +0100, Michal Nazarewicz wrote:

    Changing it to use strtol and a long prevented the overflow. It works
    properly now.
    me2, Dec 29, 2006
    #10
  11. me2 <> writes:

    > 0xffffff81 is a 32 bit number. How can it be integer overflow ?


    And its value is +0xffffff81, not -0x7f or whatever you are thinking of.
    On a host where 'int' is 32-bit, there are only 31 bits to store the
    absolute value in, and that is not enough for +0xffffff81. Thus,
    overflow.

    > Are you saying it will interpret it as an unsigned integer and then
    > try to stuff it in a signed int variable and overflow that way ?


    No, that's what the host is _not_ required to do, which is why you can't
    rely on any particular result. But yes, overflow _into_ the sign bit
    is still overflow.

    --
    Hallvard
    Hallvard B Furuseth, Dec 29, 2006
    #11
  12. me2 writes:
    > Changing it to use strtol and a long prevented the overflow. It works
    > properly now.


    Maybe 'long' is wider than 'int' on your host, then. If that's the
    reason it works, it will break on hosts where both are the same size -
    which is legal and quite common. (For that matter 'long long' is not
    required to be wider than 'int' either, but at least I don't know of any
    counterexamples.)

    --
    Hallvard
    Hallvard B Furuseth, Dec 29, 2006
    #12
  13. Hallvard B Furuseth <> writes:

    > me2 writes:
    >> Changing it to use strtol and a long prevented the overflow. It works
    >> properly now.

    >
    > Maybe 'long' is wider than 'int' on your host, then. If that's the
    > reason it works, it will break on hosts where both are the same size


    Then strtol() will set errno to appropriate value and thus program
    will print error message instead of printing wrong value (I hope).

    --
    Best regards, _ _
    .o. | Liege of Serenly Enlightened Majesty of o' \,=./ `o
    ..o | Computer Science, Michal "mina86" Nazarewicz (o o)
    ooo +--<mina86*tlen.pl>---<jid:mina86*chrome.pl>--ooO--(_)--Ooo--
    Michal Nazarewicz, Dec 31, 2006
    #13
  14. me2

    Chris Torek Guest

    >me2 wrote:
    >> ... I use sscanf %i to convert argv[1] to an int. Then
    >> I cast the int to an unsigned int and do the rest of the manipulation.
    >> However, as the above example shows, it doesn't work properly when I input
    >> a sign extended octal or hex number.
    >>
    >> The decimal of 0xffffff81 should be 4294967169, but it isn't. Why ?


    In article <>
    Scorpio <> wrote:
    >Because %i is ...


    Right start, but:

    >only for integers.


    wrong details. :)

    All of this family of conversions -- %d, %i, %o, %u, and %x -- are
    "for integers", specifically for int unless modified (e.g., %lu is
    for long, %hx is for short; note that the hh and ll modifiers are
    specific to C99). All of them also convert "as if" by strtol() or
    strtoul() -- or in C99 sometimes strtoll() and strtoull() -- with
    a base of 16, 10, 8, or 0 depending on the conversion directive;
    except that in all cases, the behavior is not defined if the number
    would overflow.

    Most implementations seem to use the strtol() family of functions
    internally, so that they all exhibit that family's "clamping"
    behavior of out-of-range inputs. This is the case above: 0xffffff81
    is (presuambly) out of range for strtol(), and %i reads a *signed*
    integer, so the input is clamped to INT_MAX (or more likely to
    LONG_MAX, but that is probably the same as INT_MAX on the target
    platform).
    --
    In-Real-Life: Chris Torek, Wind River Systems
    Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W) +1 801 277 2603
    email: forget about it http://web.torek.net/torek/index.html
    Reading email is like searching for food in the garbage, thanks to spammers.
    Chris Torek, Jan 2, 2007
    #14
  15. Chris Torek <> writes:
    [...]
    > All of this family of conversions -- %d, %i, %o, %u, and %x -- are
    > "for integers", specifically for int unless modified (e.g., %lu is
    > for long, %hx is for short; note that the hh and ll modifiers are
    > specific to C99).

    [...]

    Quibble: %d and %i are for int, %o, %u, and %x are for unsigned int.

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
    We must do something. This is something. Therefore, we must do this.
    Keith Thompson, Jan 2, 2007
    #15
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Replies:
    10
    Views:
    6,126
    Neredbojias
    Aug 19, 2005
  2. Bengt Richter
    Replies:
    6
    Views:
    445
    Juha Autero
    Aug 19, 2003
  3. kyrpa83
    Replies:
    1
    Views:
    610
    kyrpa83
    Oct 17, 2007
  4. Rob1bureau
    Replies:
    1
    Views:
    794
    joris
    Feb 27, 2010
  5. pozz
    Replies:
    12
    Views:
    712
    Tim Rentsch
    Mar 20, 2011
Loading...

Share This Page