Bug in sprintf?

Discussion in 'Ruby' started by Wes Gamble, Aug 18, 2006.

  1. Wes Gamble

    Wes Gamble Guest

    All,

    Why does this fail?

    irb(main):009:0> sprintf("%d", '08')
    ArgumentError: invalid value for Integer: "08"
    from (irb):9:in `sprintf'
    from (irb):9

    I assume it's because the 08 is being as an octal number prior to being
    changed to a decimal. 01 - 07 work fine, 09 breaks.

    But I'm telling sprintf to interpret it as decimal, so why can't I have
    leading zeroes?

    Please explain to me how this is expected behavior.

    Thanks,
    Wes

    --
    Posted via http://www.ruby-forum.com/.
    Wes Gamble, Aug 18, 2006
    #1
    1. Advertising

  2. Wes Gamble wrote:
    > I assume it's because the 08 is being as an octal number prior to being
    > changed to a decimal. 01 - 07 work fine, 09 breaks.


    Because 8 isn't an octal number. 0-7 are octal.

    --
    Posted via http://www.ruby-forum.com/.
    William Crawford, Aug 18, 2006
    #2
    1. Advertising

  3. Wes Gamble

    Guest

    On Sat, 19 Aug 2006, Wes Gamble wrote:

    > All,
    >
    > Why does this fail?
    >
    > irb(main):009:0> sprintf("%d", '08')
    > ArgumentError: invalid value for Integer: "08"
    > from (irb):9:in `sprintf'
    > from (irb):9
    >
    > I assume it's because the 08 is being as an octal number prior to being
    > changed to a decimal. 01 - 07 work fine, 09 breaks.
    >
    > But I'm telling sprintf to interpret it as decimal, so why can't I have
    > leading zeroes?
    >
    > Please explain to me how this is expected behavior.
    >
    > Thanks,
    > Wes


    because:

    harp:~ > cat a.c
    main(){ char s[42]; int r = sprintf (s, "%d", 07); printf ("%d: <%s>\n", r, s); }


    harp:~ > gcc a.c
    harp:~ > a.out
    1: <7>


    harp:~ > cat a.c
    main(){ char s[42]; int r = sprintf (s, "%d", 08); printf ("%d: <%s>\n", r, s); }


    harp:~ > gcc a.c
    a.c: In function `main':
    a.c:1: numeric constant contains digits beyond the radix


    ruby uses 'Integer(s)' to convert strings to ints and


    harp:~ > ri Kernel.Integer
    --------------------------------------------------------- Kernel#Integer
    Integer(arg) => integer
    ------------------------------------------------------------------------
    Converts _arg_ to a +Fixnum+ or +Bignum+. Numeric types are
    converted directly (with floating point numbers being truncated).
    If _arg_ is a +String+, leading radix indicators (+0+, +0b+, and
    +0x+) are honored. Others are converted using +to_int+ and +to_i+.
    This behavior is different from that of +String#to_i+.

    Integer(123.999) #=> 123
    Integer("0x1a") #=> 26
    Integer(Time.new) #=> 1049896590


    harp:~ > ruby -e' Integer "07" '

    harp:~ > ruby -e' Integer "08" '
    -e:1:in `Integer': invalid value for Integer: "08" (ArgumentError)
    from -e:1

    so it's expected, documented, and consistent with c.

    a good pattern to use is:


    harp:~ > ruby -e' atoi = lambda{|s| Integer(s) rescue %r/(\d+)/.match(s)[1].to_i rescue raise ArgumentError, s}; p(atoi["08"]); p(atoi["fubar"]) '
    8
    -e:1: fubar (ArgumentError)
    from -e:1


    because simply falling back on to_i yields


    harp:~ > ruby -e' atoi = lambda{|s| Integer(s) rescue s.to_i}; p(atoi["08"]); p(atoi["fubar"]) '
    8
    0


    regards.

    -a
    --
    to foster inner awareness, introspection, and reasoning is more efficient than
    meditation and prayer.
    - h.h. the 14th dali lama
    , Aug 18, 2006
    #3
  4. William Crawford wrote:
    > Wes Gamble wrote:
    >> I assume it's because the 08 is being as an octal number prior to being
    >> changed to a decimal. 01 - 07 work fine, 09 breaks.

    >
    > Because 8 isn't an octal number. 0-7 are octal.


    -slaps self- Think before you reply, William.

    On second thought, this -does- seem wrong. It doesn't matter if you
    tell it to be decimal, octal or hex, it all comes up with an error on 08
    and 09, and not 00 to 07.

    --
    Posted via http://www.ruby-forum.com/.
    William Crawford, Aug 18, 2006
    #4
  5. William Crawford wrote:
    > William Crawford wrote:
    >> Wes Gamble wrote:
    >>> I assume it's because the 08 is being as an octal number prior to being
    >>> changed to a decimal. 01 - 07 work fine, 09 breaks.

    >>
    >> Because 8 isn't an octal number. 0-7 are octal.

    >
    > -slaps self- Think before you reply, William.
    >
    > On second thought, this -does- seem wrong. It doesn't matter if you
    > tell it to be decimal, octal or hex, it all comes up with an error on 08
    > and 09, and not 00 to 07.


    Last time, I swear.
    http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/145509 Says
    that it IS trying to be octal because the 0 in front means that. (Why
    it means that, I don't know.)

    --
    Posted via http://www.ruby-forum.com/.
    William Crawford, Aug 18, 2006
    #5
  6. Wes Gamble

    Carlos Guest

    wrote:

    > On Sat, 19 Aug 2006, Wes Gamble wrote:
    >
    >> All,
    >>
    >> Why does this fail?
    >>
    >> irb(main):009:0> sprintf("%d", '08')
    >> ArgumentError: invalid value for Integer: "08"
    >> from (irb):9:in `sprintf'
    >> from (irb):9

    [...]
    >> But I'm telling sprintf to interpret it as decimal, so why can't I have
    >> leading zeroes?
    >>
    >> Please explain to me how this is expected behavior.

    [...]
    > because:

    [...]
    > ruby uses 'Integer(s)' to convert strings to ints and

    [...]
    > Kernel#Integer

    [...]
    > If _arg_ is a +String+, leading radix indicators (+0+, +0b+, and
    > +0x+) are honored.

    [...]
    > so it's expected, documented, and consistent with c.


    It is not expected, nor documented, because the documentation of sprintf
    says:
    d | Convert argument as a decimal number.

    ...which is very explicit in that the argument will be interpreted as a
    decimal number, not octal.

    Greetings.
    Carlos, Aug 18, 2006
    #6
  7. Wes Gamble

    Wes Gamble Guest

    Thanks for the comprehensive response.

    Well, I can't really argue with the C implementation, of course...

    BUT

    it feels like if I declare my intent to interpret the sprintf input as a
    _decimal_ number with %d (there's a %o if I wanted octal), that is
    should allow for leading zeroes.

    My intent in writing sprintf("%d", '08') is to say take the string 08
    and turn it into the number 8.

    It isn't to say here's an octal number, please convert it to decimal for
    me. If I think the string is octal, I'll use %o for that.

    So in the spirit of what the user expects, I still think this smells
    bad.

    Feel free to convince me otherwise.

    Thanks again,
    Wes

    --
    Posted via http://www.ruby-forum.com/.
    Wes Gamble, Aug 18, 2006
    #7
  8. Wes Gamble

    Wes Gamble Guest

    Wes Gamble, Aug 18, 2006
    #8
  9. Wes Gamble

    Jan Svitok Guest

    On 8/18/06, Wes Gamble <> wrote:
    > Thanks for the comprehensive response.
    >
    > Well, I can't really argue with the C implementation, of course...
    >
    > BUT
    >
    > it feels like if I declare my intent to interpret the sprintf input as a
    > _decimal_ number with %d (there's a %o if I wanted octal), that is
    > should allow for leading zeroes.
    >
    > My intent in writing sprintf("%d", '08') is to say take the string 08
    > and turn it into the number 8.
    >
    > It isn't to say here's an octal number, please convert it to decimal for
    > me. If I think the string is octal, I'll use %o for that.
    >
    > So in the spirit of what the user expects, I still think this smells
    > bad.
    >
    > Feel free to convince me otherwise.
    >
    > Thanks again,
    > Wes
    >


    As I see it, there are two separate things: WHAT number you want to
    print, and HOW you want it print.

    The latter one is specified by the format string.
    The former one is specified by argument.

    Think of sprintf("%d", 0x1234) or sprintf("%x", 123).

    And it happens to be that '08' is in this case the same as 08 and that
    is invalid octal number. QED. ;-)

    Jano
    Jan Svitok, Aug 18, 2006
    #9
  10. Carlos wrote:

    > It is not expected, nor documented, because the documentation of sprintf
    > says:
    > d | Convert argument as a decimal number.
    >
    > ...which is very explicit in that the argument will be interpreted as a
    > decimal number, not octal.


    I disagree on that. %d tells sprintf to expect an Integer as the
    corresponding argument. Being given a String instead it tries to convert
    it by calling Kernel#Integer. This fails for the reasons already
    metioned.

    Greetings, Matthias


    --
    Posted via http://www.ruby-forum.com/.
    Matthias Reitinger, Aug 18, 2006
    #10
  11. Carlos <> writes:

    > wrote:
    >
    >> On Sat, 19 Aug 2006, Wes Gamble wrote:
    >>
    >> so it's expected, documented, and consistent with c.

    >
    > It is not expected, nor documented, because the documentation of
    > sprintf says:
    > d | Convert argument as a decimal number.
    >
    > ...which is very explicit in that the argument will be interpreted as
    > a decimal number, not octal.


    I think that the problem here is that ruby is performing two
    conversions, and the sprintf documentation only mentions one of them.

    When you use '%d' in a sprintf(), ruby does a conversion of the
    argument to an Integer using Kernel#Integer and then does the
    conversion of the Integer to a portion of the output string as
    documented.

    Similarly, when you use %f in a sprintf(), ruby does a conversion of
    the argument using Kernel#float and then does the conversion from
    Float to part of the output string as documented.

    So, we have:

    sprintf("%d",'08') ==> ArgumentError
    sprintf("%d",'8') ==> "8"
    sprintf("%d",'08'.to_i) ==> "8"
    sprintf("%f",'08') ==> "8.000000"

    I think that if you're hitting this quirk in actual code, your code
    needs so .to_i dropped in a few places. I'm actually surprised ruby
    does those String=>Integer conversions at all.
    Daniel Martin, Aug 18, 2006
    #11
  12. Wes Gamble

    L7 Guest

    Wes Gamble wrote:
    > Thanks for the comprehensive response.
    >
    > Well, I can't really argue with the C implementation, of course...
    >
    > BUT
    >
    > it feels like if I declare my intent to interpret the sprintf input as a
    > _decimal_ number with %d (there's a %o if I wanted octal), that is
    > should allow for leading zeroes.
    >
    > My intent in writing sprintf("%d", '08') is to say take the string 08
    > and turn it into the number 8.
    >
    > It isn't to say here's an octal number, please convert it to decimal for
    > me. If I think the string is octal, I'll use %o for that.
    >
    > So in the spirit of what the user expects, I still think this smells
    > bad.
    >
    > Feel free to convince me otherwise.
    >


    Consider it this way:

    The format character (%d) is a way to know how the values collected
    will be displayed. The values, in this case arguments to %d, are not
    intrepreted (necessarily) at the same time as the format characters and
    therefor are seperate entities. Taking any [legal] value and passing it
    to %d will result in a decimal number printed out.
    However, the parsing aspect is entirely different. There is no format
    that says: 'this is how to interpret me' - other than the syntax.
    If you take that away you create ambiguities. If the syntax is longer
    how to distinguish a value, it must be explicitly tied to a format
    string/character.

    Imagine the confusion when you try to
    #define FORTY_TWO_HEX %x 2a
    #define FORTY_TWO_OCT %o 52
    #define FORTY_TOW_DEC %d 42
    (Since the two are now linked, you need the format characters. Or
    should it be %x 42, %o 42 ... you get the idea)

    instead of
    #define FORTY_TWO_HEX 0x2a
    #define FORTY_TWO_OCT 052
    #define FORTY_TWO_DEC 42

    While it may be easy/convenient to suggest allowing a format character
    determine a value, it breaks when not in a specific setting.



    > Thanks again,
    > Wes
    >
    > --
    > Posted via http://www.ruby-forum.com/.
    L7, Aug 18, 2006
    #12
  13. Wes Gamble

    Wes Gamble Guest

    I'll buy that. I did know that my input was a String so I am implicitly
    asking for a conversion to occur.

    Thanks for the discussion - it helps a lot.

    Wes

    --
    Posted via http://www.ruby-forum.com/.
    Wes Gamble, Aug 18, 2006
    #13
  14. Wes Gamble

    Xavier Noria Guest

    On Aug 18, 2006, at 8:00 PM, Daniel Martin wrote:

    > When you use '%d' in a sprintf(), ruby does a conversion of the
    > argument to an Integer using Kernel#Integer and then does the
    > conversion of the Integer to a portion of the output string as
    > documented.


    How did you figure that out? I started at sprintf.c and browsed a bit
    around from rb_f_sprintf on, but I think I was in the wrong path. Or
    is it the call to rb_str_to_inum equivalent to Kernel#Integer?

    -- fxn
    Xavier Noria, Aug 18, 2006
    #14
  15. Wes Gamble wrote:
    > it feels like if I declare my intent to interpret the sprintf input as a
    > _decimal_ number with %d (there's a %o if I wanted octal), that is
    > should allow for leading zeroes.


    Declare your intent by doing '08'.to_i(10) instead. In fact, declare
    your intent in the first place that way.

    Expecting sprintf to be automagical the way you expect out of two
    incompatible, but perfectly valid interpretations isn't much to rely on.
    Code everything explicitly and you'll get predictable results.

    I'll join the club of people wondering why the String->Integer coercion
    is happening in the first place.

    David Vallner
    David Vallner, Aug 18, 2006
    #15
  16. Wes Gamble

    Hal Fulton Guest

    William Crawford wrote:
    >
    > Last time, I swear.
    > http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/145509 Says
    > that it IS trying to be octal because the 0 in front means that. (Why
    > it means that, I don't know.)
    >


    This comes from C, and is at least 30 years old.

    The zero looks like an O, which stands for Octal.

    Be glad it's not Pascal, where 377B is an octal
    number (because B looks like an 8).


    Hal
    Hal Fulton, Aug 18, 2006
    #16
  17. Wes Gamble

    Hal Fulton Guest

    Carlos wrote:
    >
    >> If _arg_ is a +String+, leading radix indicators (+0+, +0b+, and
    >> +0x+) are honored.

    >
    > [...]
    >
    >> so it's expected, documented, and consistent with c.

    >
    >
    > It is not expected, nor documented, because the documentation of sprintf
    > says:
    > d | Convert argument as a decimal number.
    >
    > ...which is very explicit in that the argument will be interpreted as a
    > decimal number, not octal.


    The string of characters is interpreted as a decimal number IF IT IS
    one. Since you started it with a zero, it's octal.

    Hal
    Hal Fulton, Aug 18, 2006
    #17
  18. Wes Gamble

    Hal Fulton Guest

    Wes Gamble wrote:
    > Thanks for the comprehensive response.
    >
    > Well, I can't really argue with the C implementation, of course...
    >
    > BUT
    >
    > it feels like if I declare my intent to interpret the sprintf input as a
    > _decimal_ number with %d (there's a %o if I wanted octal), that is
    > should allow for leading zeroes.
    >
    > My intent in writing sprintf("%d", '08') is to say take the string 08
    > and turn it into the number 8.
    >
    > It isn't to say here's an octal number, please convert it to decimal for
    > me. If I think the string is octal, I'll use %o for that.
    >
    > So in the spirit of what the user expects, I still think this smells
    > bad.
    >
    > Feel free to convince me otherwise.


    I see your point. But most things in Ruby that are stolen from C
    behave JUST like the C counterparts. In fact, in many cases they
    are just wrappers.

    So I can understand why you don't like it. But my understanding
    is that you should blame C.


    Hal
    Hal Fulton, Aug 18, 2006
    #18
  19. Wes Gamble

    Hal Fulton Guest

    Wes Gamble wrote:
    > I'll buy that. I did know that my input was a String so I am implicitly
    > asking for a conversion to occur.
    >
    > Thanks for the discussion - it helps a lot.


    Actually I failed to take that into account, so what I said
    was half nonsense. :)

    Hal
    Hal Fulton, Aug 18, 2006
    #19
  20. Wes Gamble

    Carlos Guest

    [Hal Fulton <>, 2006-08-19 00.27 CEST]
    > Carlos wrote:
    > >
    > >> If _arg_ is a +String+, leading radix indicators (+0+, +0b+, and
    > >> +0x+) are honored.

    > >
    > >[...]
    > >
    > >>so it's expected, documented, and consistent with c.

    > >
    > >
    > >It is not expected, nor documented, because the documentation of sprintf
    > >says:
    > > d | Convert argument as a decimal number.
    > >
    > >...which is very explicit in that the argument will be interpreted as a
    > >decimal number, not octal.

    >
    > The string of characters is interpreted as a decimal number IF IT IS
    > one. Since you started it with a zero, it's octal.


    It's a string... I wanted this string formatted as a decimal number. Just
    put these digits right justified in that other string. All this
    behind-the-scenes conversion to integer is an implementation detail I should
    not care about.

    For that conversion, #to_i could have been used as well, and then '09' would
    be interpreted as decimal 9. Why should I expect my string to be passed
    through Integer() before formatting? That is not documented.

    So, I expected
    sprintf "%02d", "09" # => "09"

    I see that none of you (except Wes) expected that, so, OK, my expectations
    should be wrong. I really didn't expect _that_ (but I'm bad on expectations
    :), I supposed that my expectations would be the majority, or that at
    least 50% would expect something like #to_i.

    But everyone expected Integer() and Integer() is what is used, and this took
    nobody by surprise, so it's OK, I guess :)

    Greetings.
    Carlos, Aug 19, 2006
    #20
    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. Mark Hubbart

    sprintf bug in 1.9.0?

    Mark Hubbart, Feb 16, 2004, in forum: Ruby
    Replies:
    5
    Views:
    224
  2. jwesley

    Sprintf bug

    jwesley, Nov 11, 2005, in forum: Ruby
    Replies:
    8
    Views:
    167
  3. Replies:
    4
    Views:
    190
    Leslie Viljoen
    Jul 29, 2006
  4. Peter Szinek

    sprintf bug (?)

    Peter Szinek, Nov 11, 2006, in forum: Ruby
    Replies:
    8
    Views:
    183
    Bernard Kenik
    Nov 14, 2006
  5. Daniel DeLorme

    sprintf bug?

    Daniel DeLorme, Feb 1, 2008, in forum: Ruby
    Replies:
    1
    Views:
    93
    Robert Klemme
    Feb 1, 2008
Loading...

Share This Page