summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog5
-rw-r--r--sprintf.c52
-rw-r--r--test/ruby/test_sprintf.rb14
3 files changed, 58 insertions, 13 deletions
diff --git a/ChangeLog b/ChangeLog
index d719f43c51..92ae73671b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+Mon Sep 15 08:13:40 2014 Matthew Draper <matthew@trebex.net>
+
+ * sprintf.c (rb_str_format): rational 'f' format works for more
+ values. [fix GH-717]
+
Sun Sep 14 16:57:27 2014 Eric Wong <e@80x24.org>
* template/vm.inc.tmpl: "insns.c" => "insns.def"
diff --git a/sprintf.c b/sprintf.c
index d2bc9f89b5..b73aabd823 100644
--- a/sprintf.c
+++ b/sprintf.c
@@ -1027,6 +1027,7 @@ rb_str_format(int argc, const VALUE *argv, VALUE fmt)
VALUE val = GETARG(), num, den;
int sign = (flags&FPLUS) ? 1 : 0, zero = 0;
long len;
+ int i, done = 0, prefix = 0;
if (!RB_TYPE_P(val, T_RATIONAL)) {
nextvalue = val;
goto float_value;
@@ -1062,28 +1063,53 @@ rb_str_format(int argc, const VALUE *argv, VALUE fmt)
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 ? '-' : ' ';
+ prefix++;
+ done++;
}
- if (sign || (flags&FSPACE)) buf[blen++] = sign > 0 ? '+' : sign < 0 ? '-' : ' ';
len = RSTRING_LEN(val) + zero;
t = RSTRING_PTR(val);
- if (len > prec)
+ if (len > prec) {
memcpy(&buf[blen], t, len - prec);
- else
+ blen += len - prec;
+ done += len - prec;
+ }
+ else {
buf[blen++] = '0';
- blen += len - prec;
- if (prec > 0) buf[blen++] = '.';
- if (zero) FILL('0', zero);
+ done++;
+ }
+ if (prec > 0) {
+ buf[blen++] = '.';
+ done++;
+ }
+ if (zero) {
+ FILL('0', zero);
+ done += zero;
+ }
+ else if (prec > len) {
+ FILL('0', prec - len);
+ memcpy(&buf[blen], t, len);
+ blen += len;
+ done += prec;
+ }
else if (prec > 0) {
memcpy(&buf[blen], t + len - prec, prec);
blen += prec;
+ done += prec;
+ }
+ if ((flags & FWIDTH) && width > done) {
+ if (!(flags&FMINUS)) {
+ int shifting = (flags&FZERO) ? done - prefix : done;
+ for (i = 1; i <= shifting; i++)
+ buf[width - i] = buf[done - i];
+ blen -= shifting;
+ FILL((flags&FZERO) ? '0' : ' ', width - done);
+ blen += shifting;
+ } else {
+ FILL(' ', width - done);
+ }
}
- if (width > 0) FILL(' ', width);
RB_GC_GUARD(val);
break;
}
diff --git a/test/ruby/test_sprintf.rb b/test/ruby/test_sprintf.rb
index 0b6bfb0c08..9292155c6b 100644
--- a/test/ruby/test_sprintf.rb
+++ b/test/ruby/test_sprintf.rb
@@ -150,8 +150,22 @@ class TestSprintf < Test::Unit::TestCase
def test_rational
assert_match(/\A0\.10+\z/, sprintf("%.60f", 0.1r))
+ assert_match(/\A0\.010+\z/, sprintf("%.60f", 0.01r))
+ assert_match(/\A0\.0010+\z/, sprintf("%.60f", 0.001r))
assert_match(/\A0\.3+\z/, sprintf("%.60f", 1/3r))
assert_match(/\A1\.20+\z/, sprintf("%.60f", 1.2r))
+
+ 0.upto(9) do |len|
+ -1.upto(9) do |prec|
+ ['', '+', '-', ' ', '0', '+0', '-0', ' 0', '+ ', '- ', '+ 0', '- 0'].each do |flags|
+ fmt = "%#{flags}#{len > 0 ? len : ''}#{prec >= 0 ? ".#{prec}" : ''}f"
+ [0, 0.1, 0.01, 0.001, 1.001, 100.0, 100.001, 10000000000.0, 0.00000000001, 1/3r, 2/3r, 1.2r, 10r].each do |num|
+ assert_equal(sprintf(fmt, num.to_f), sprintf(fmt, num.to_r), "sprintf(#{fmt.inspect}, #{num.inspect}.to_r)")
+ assert_equal(sprintf(fmt, -num.to_f), sprintf(fmt, -num.to_r), "sprintf(#{fmt.inspect}, #{(-num).inspect}.to_r)") if num > 0
+ end
+ end
+ end
+ end
end
def test_hash