Forum: Ruby Bug in sprintf?

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Bb4bdf2b184027bc38d4fb529770cde5?d=identicon&s=25 Wes Gamble (weyus)
on 2006-08-18 18:39
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
E3f82b918ccd3c6d7b63a54bea9ff60b?d=identicon&s=25 William Crawford (aladrin)
on 2006-08-18 19:00
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.
E3f82b918ccd3c6d7b63a54bea9ff60b?d=identicon&s=25 William Crawford (aladrin)
on 2006-08-18 19:04
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.
E3f82b918ccd3c6d7b63a54bea9ff60b?d=identicon&s=25 William Crawford (aladrin)
on 2006-08-18 19:05
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/...  Says
that it IS trying to be octal because the 0 in front means that.  (Why
it means that, I don't know.)
Cb48ca5059faf7409a5ab3745a964696?d=identicon&s=25 unknown (Guest)
on 2006-08-18 19:09
(Received via mailing list)
On Sat, 19 Aug 2006, Wes Gamble wrote:

> 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
C2cd72c24873556e278b44b5b3c7ef33?d=identicon&s=25 Carlos (Guest)
on 2006-08-18 19:24
(Received via mailing list)
ara.t.howard@noaa.gov 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.
Bb4bdf2b184027bc38d4fb529770cde5?d=identicon&s=25 Wes Gamble (weyus)
on 2006-08-18 19:27
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
Bb4bdf2b184027bc38d4fb529770cde5?d=identicon&s=25 Wes Gamble (weyus)
on 2006-08-18 19:28
What Carlos said ;)
97550977337c9f0a0e1a9553e55bfaa0?d=identicon&s=25 Jan Svitok (Guest)
on 2006-08-18 19:42
(Received via mailing list)
On 8/18/06, Wes Gamble <weyus@att.net> wrote:
> My intent in writing sprintf("%d", '08') is to say take the string 08
> 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
09348009e57e24e10bbc08d925bf69ca?d=identicon&s=25 Matthias Reitinger (reima)
on 2006-08-18 19:53
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
Cf6d0868b2b4c69bac3e6f265a32b6a7?d=identicon&s=25 Daniel Martin (Guest)
on 2006-08-18 20:03
(Received via mailing list)
Carlos <angus@quovadis.com.ar> writes:

> ...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.
Bb4bdf2b184027bc38d4fb529770cde5?d=identicon&s=25 Wes Gamble (weyus)
on 2006-08-18 20:26
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
4c885431ce14d956fdb08cc45f1ff325?d=identicon&s=25 L7 (Guest)
on 2006-08-18 20:27
(Received via mailing list)
Wes Gamble wrote:
> 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.
7223c62b7310e164eb79c740188abbda?d=identicon&s=25 Xavier Noria (Guest)
on 2006-08-18 21:42
(Received via mailing list)
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
430ea1cba106cc65b7687d66e9df4f06?d=identicon&s=25 David Vallner (Guest)
on 2006-08-18 21:48
(Received via mailing list)
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
C1bcb559f87f356698cfad9f6d630235?d=identicon&s=25 Hal Fulton (Guest)
on 2006-08-19 00:27
(Received via mailing list)
William Crawford wrote:
>
> Last time, I swear.
> http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/...  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
C1bcb559f87f356698cfad9f6d630235?d=identicon&s=25 Hal Fulton (Guest)
on 2006-08-19 00:30
(Received via mailing list)
Carlos wrote:
> 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
C1bcb559f87f356698cfad9f6d630235?d=identicon&s=25 Hal Fulton (Guest)
on 2006-08-19 00:30
(Received via mailing list)
Wes Gamble wrote:
> 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
C1bcb559f87f356698cfad9f6d630235?d=identicon&s=25 Hal Fulton (Guest)
on 2006-08-19 00:42
(Received via mailing list)
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
C2cd72c24873556e278b44b5b3c7ef33?d=identicon&s=25 Carlos (Guest)
on 2006-08-19 03:21
(Received via mailing list)
[Hal Fulton <hal9000@hypermetrics.com>, 2006-08-19 00.27 CEST]
> >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.
C1bcb559f87f356698cfad9f6d630235?d=identicon&s=25 Hal Fulton (Guest)
on 2006-08-19 03:23
(Received via mailing list)
Carlos wrote:
>
> 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.
>

Why would you pass a string to something that expects a number
anyway?


Hal
C1bcb559f87f356698cfad9f6d630235?d=identicon&s=25 Hal Fulton (Guest)
on 2006-08-19 03:27
(Received via mailing list)
Hal Fulton wrote:
> anyway?
Actually, let me say this, as it's more informative and
constructive.

If you need to convert your string to a number, it's best
to do so explicitly. (If you really want to pass a string
into sprintf, %s is the correct modifier.)

Also note that while Integer() doesn't convert the way
you want, to_i does:

    Integer("09")  # error
    "09".to_i      # 9


Cheers,
Hal
703fbc991fd63e0e1db54dca9ea31b53?d=identicon&s=25 Robert Dober (Guest)
on 2006-08-19 10:10
(Received via mailing list)
On 8/19/06, Hal Fulton <hal9000@hypermetrics.com> wrote:
> >
> Also note that while Integer() doesn't convert the way
>
Hal
all you are saying is true BUT
I do not think we should blame C for something which really is not nice.
I know not many share my opinion but the brave speaks out nevertheless
;)

IMHO the interpreter should raise an expression when a String is passed
to
%d (as you asked OP rightfully not to do so).
Early Failure, please, *early*.

BTW who is talking about sprintf I am talking about "%" a very rubish
construct.

Cheers
Robert



--
Deux choses sont infinies : l'univers et la bêtise humaine ; en ce qui
concerne l'univers, je n'en ai pas acquis la certitude absolue.

- Albert Einstein
31ab75f7ddda241830659630746cdd3a?d=identicon&s=25 Austin Ziegler (austin)
on 2006-08-19 15:38
(Received via mailing list)
On 8/19/06, Robert Dober <robert.dober@gmail.com> wrote:
> BTW who is talking about sprintf I am talking about "%" a very rubish
> construct.

These are equivalent:

  sprintf("%02d", 3)
  "%02d" % 3

-austin
C1bcb559f87f356698cfad9f6d630235?d=identicon&s=25 Hal Fulton (Guest)
on 2006-08-20 06:40
(Received via mailing list)
Robert Dober wrote:
>>
> Hal
> all you are saying is true BUT
> I do not think we should blame C for something which really is not nice.
> I know not many share my opinion but the brave speaks out nevertheless ;)
>
> IMHO the interpreter should raise an expression when a String is passed to
> %d (as you asked OP rightfully not to do so).
> Early Failure, please, *early*.

I see your point. Truthfully, I think automatic conversion of a
string in this case is probably a bad idea. And that is a Ruby
issue, not a C one.

> BTW who is talking about sprintf I am talking about "%" a very rubish
> construct.

But  format % array behaves the same as sprintf(format,*array)

The specifiers are the same (and the same as C, perhaps with a few
minor differences).


Hal
703fbc991fd63e0e1db54dca9ea31b53?d=identicon&s=25 Robert Dober (Guest)
on 2006-08-20 10:54
(Received via mailing list)
On 8/20/06, Hal Fulton <hal9000@hypermetrics.com> wrote:
> to
> But  format % array behaves the same as sprintf(format,*array)
Yup, I failed to be clear about this sorry for wasting Austin's and your
time.
I should have said
   even more so as we have the % operator which looking very rubish
still
has these
   C caveats.

Anyway I am more than happy that you share my POV ;-)

Cheers
Robert

The specifiers are the same (and the same as C, perhaps with a few
This topic is locked and can not be replied to.