From 1d196e0d2bd99590d03a73d7e59aa87f7266f8e3 Mon Sep 17 00:00:00 2001 From: nobu Date: Mon, 18 Aug 2014 08:06:48 +0000 Subject: sprintf.c: rational 'f' format * sprintf.c (rb_str_format): support rational 'f' format. [ruby-core:64382] [Bug #10136] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@47214 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 5 ++++ common.mk | 2 +- sprintf.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++ test/ruby/test_sprintf.rb | 5 ++++ 4 files changed, 78 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index be0d178e6f..7417a98d2d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +Mon Aug 18 17:06:27 2014 Nobuyoshi Nakada + + * sprintf.c (rb_str_format): support rational 'f' format. + [ruby-core:64382] [Bug #10136] + Mon Aug 18 08:03:46 2014 SHIBATA Hiroshi * spec/default.mspec: use 2.2 definition. diff --git a/common.mk b/common.mk index 6c7d43631a..8419314d7a 100644 --- a/common.mk +++ b/common.mk @@ -771,7 +771,7 @@ ruby.$(OBJEXT): {$(VPATH)}ruby.c $(RUBY_H_INCLUDES) {$(VPATH)}util.h \ safe.$(OBJEXT): {$(VPATH)}safe.c $(RUBY_H_INCLUDES) $(VM_CORE_H_INCLUDES) {$(VPATH)}vm_opts.h {$(VPATH)}internal.h signal.$(OBJEXT): {$(VPATH)}signal.c $(RUBY_H_INCLUDES) \ $(VM_CORE_H_INCLUDES) {$(VPATH)}vm_opts.h {$(VPATH)}internal.h {$(VPATH)}ruby_atomic.h {$(VPATH)}eval_intern.h -sprintf.$(OBJEXT): {$(VPATH)}sprintf.c $(RUBY_H_INCLUDES) {$(VPATH)}re.h \ +sprintf.$(OBJEXT): {$(VPATH)}sprintf.c $(RUBY_H_INCLUDES) {$(VPATH)}re.h {$(VPATH)}id.h \ {$(VPATH)}regex.h {$(VPATH)}vsnprintf.c $(ENCODING_H_INCLUDES) {$(VPATH)}internal.h st.$(OBJEXT): {$(VPATH)}st.c $(RUBY_H_INCLUDES) strftime.$(OBJEXT): {$(VPATH)}strftime.c $(RUBY_H_INCLUDES) \ diff --git a/sprintf.c b/sprintf.c index 69920c6443..19338fb62e 100644 --- a/sprintf.c +++ b/sprintf.c @@ -15,6 +15,7 @@ #include "ruby/re.h" #include "ruby/encoding.h" #include "internal.h" +#include "id.h" #include #include @@ -1022,12 +1023,78 @@ rb_str_format(int argc, const VALUE *argv, VALUE fmt) break; case 'f': + { + VALUE val = GETARG(), num, den; + int sign = (flags&FPLUS) ? 1 : 0, zero = 0; + long len; + if (!RB_TYPE_P(val, T_RATIONAL)) { + nextvalue = val; + goto float_value; + } + if (!(flags&FPREC)) prec = default_float_precision; + den = rb_rational_den(val); + num = rb_rational_num(val); + if (FIXNUM_P(num)) { + if ((SIGNED_VALUE)num < 0) { + long n = -FIX2LONG(num); + num = LONG2FIX(n); + sign = -1; + } + } + else if (rb_num_negative_p(num)) { + sign = -1; + num = rb_funcallv(num, idUMinus, 0, 0); + } + if (den != INT2FIX(1) && prec > 1) { + const ID idDiv = rb_intern("div"); + VALUE p10 = rb_int_positive_pow(10, prec); + VALUE den_2 = rb_funcall(den, idDiv, 1, INT2FIX(2)); + num = rb_funcallv(num, '*', 1, &p10); + num = rb_funcallv(num, '+', 1, &den_2); + num = rb_funcallv(num, idDiv, 1, &den); + } + else if (prec >= 0) { + zero = prec; + } + val = rb_obj_as_string(num); + len = RSTRING_LEN(val) + zero; + if (prec >= len) ++len; /* integer part 0 */ + if (sign || (flags&FSPACE)) ++len; + if (prec > 0) ++len; /* period */ + CHECK(len > width ? len : width); + if (width > len) { + width -= (int)len; + if (!(flags&FMINUS)) { + FILL(' ', width); + width = 0; + } + } + if (sign || (flags&FSPACE)) buf[blen++] = sign > 0 ? '+' : sign < 0 ? '-' : ' '; + len = RSTRING_LEN(val) + zero; + t = RSTRING_PTR(val); + if (len > prec) + memcpy(&buf[blen], t, len - prec); + else + buf[blen++] = '0'; + blen += len - prec; + if (prec > 0) buf[blen++] = '.'; + if (zero) FILL('0', zero); + else if (prec > 0) { + memcpy(&buf[blen], t + len - prec, prec); + blen += prec; + } + if (width > 0) FILL(' ', width); + RB_GC_GUARD(val); + break; + } case 'g': case 'G': case 'e': case 'E': + /* TODO: rational support */ case 'a': case 'A': + float_value: { VALUE val = GETARG(); double fval; diff --git a/test/ruby/test_sprintf.rb b/test/ruby/test_sprintf.rb index 7994e99e5e..3cfb9ac7db 100644 --- a/test/ruby/test_sprintf.rb +++ b/test/ruby/test_sprintf.rb @@ -148,6 +148,11 @@ class TestSprintf < Test::Unit::TestCase assert_equal(" Inf", sprintf("% e", inf), '[ruby-dev:34002]') end + def test_rational + assert_match(/\A0\.10+\z/, sprintf("%.60f", 0.1r)) + assert_match(/\A0\.3+\z/, sprintf("%.60f", 1/3r)) + end + def test_hash options = {:capture=>/\d+/} assert_equal("with options {:capture=>/\\d+/}", sprintf("with options %p" % options)) -- cgit v1.2.3