Project

General

Profile

Feature #12244

Updated by naruse (Yui NARUSE) almost 8 years ago

We sometimes calculates `integer - integer % num`. 

 For example time series events into time partitions, we write code like 

 ```ruby 
 event # {time: 1459580435, name: "hoge", text: "Rawr!"} 
 partition = event[:time] - event[:time] % num 
 chunk = get_chunk(partition) 
 chunk.write event 
 ``` 
 https://twitter.com/tagomoris/status/715814050534461440 
 https://twitter.com/tagomoris/status/715814961260457985 


 The name is always issue. 
 There are some suggestions likes Integer#adjust](https://twitter.com/cocoatomo/status/716088708655489024). 

 kosaki says [Excel's FLOOR() function is FLOOR(number, significance)](https://twitter.com/kosaki55tea/status/716059296186765312). 
 Therefore Ruby should be [Integer#floor(digits=1, significance: nil)](https://twitter.com/kosaki55tea/status/716106530114768897). 
 [kosaki agrees this](https://twitter.com/kosaki55tea/status/716107516409581568). 

 I checked the speed of a half baked implementation..., but it's 10x slow... 

 
 ``` 
 % time ./miniruby -e'i=10000000;while i>0;i-=1;1459497599.floor(significance: 3600);end' 
 ./miniruby     6.58s user 0.02s system 99% cpu 6.596 total 
 #3 1459604131 22:35:31 naruse@windy:~/obj/ruby 
 % time ./miniruby -e'i=10000000;while i>0;i-=1;t=1459497599;t-t%3600;end' 
 ./miniruby -e'i=10000000;while i>0;i-=1;t=1459497599;t-t%3600;end'    0.52s user 0.00s system 99% cpu 0.520 total 
 ``` 


 ```diff 
 diff --git a/numeric.c b/numeric.c 
 index 37217a1..f6acfe3 100644 
 --- a/numeric.c 
 +++ b/numeric.c 
 @@ -4123,6 +4123,42 @@ int_dotimes(VALUE num) 

  /* 
   *    call-seq: 
 + *       int.floor([ndigits])    ->    integer or float 
 + * 
 + *    Rounds +int+ to a given precision in decimal digits (default 0 digits). 
 + * 
 + *    Precision may be negative.    Returns a floating point number when +ndigits+ 
 + *    is positive, +self+ for zero, and round down for negative. 
 + * 
 + *       1.round          #=> 1 
 + *       1.round(2)       #=> 1.0 
 + *       15.round(-1)     #=> 20 
 + */ 
 + 
 +static VALUE 
 +int_floor(int argc, VALUE* argv, VALUE num) 
 +{ 
 +      static ID keyword_ids[1]; 
 +      VALUE kwargs[1], ndigits, opt; 
 +      if (!keyword_ids[0]) { 
 +         CONST_ID(keyword_ids[0], "significance"); 
 +      } 
 + 
 +      rb_scan_args(argc, argv, "01:", &ndigits, &opt); 
 +      if (!NIL_P(opt)) { 
 +         VALUE factor; 
 +         long a, b; 
 +         rb_get_kwargs(opt, keyword_ids, 0, 1, kwargs); 
 +         factor = kwargs[0]; 
 +         a = FIX2LONG(num); 
 +         b = FIX2LONG(factor); 
 +         return LONG2FIX(a - a % b); 
 +      } 
 +      return Qnil; 
 +} 
 + 
 +/* 
 + *    call-seq: 
   *       int.round([ndigits])    ->    integer or float 
   * 
   *    Rounds +int+ to a given precision in decimal digits (default 0 digits). 
 @@ -4321,7 +4357,7 @@ Init_Numeric(void) 
      rb_define_method(rb_cInteger, "to_i", int_to_i, 0); 
      rb_define_method(rb_cInteger, "to_int", int_to_i, 0); 
      rb_define_method(rb_cInteger, "to_f", int_to_f, 0); 
 -      rb_define_method(rb_cInteger, "floor", int_to_i, 0); 
 +      rb_define_method(rb_cInteger, "floor", int_floor, -1); 
      rb_define_method(rb_cInteger, "ceil", int_to_i, 0); 
      rb_define_method(rb_cInteger, "truncate", int_to_i, 0); 
      rb_define_method(rb_cInteger, "round", int_round, -1);

Back