fraction 0.1 → 0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. data/README.md +45 -0
  2. data/fraction.c +68 -6
  3. metadata +18 -8
@@ -0,0 +1,45 @@
1
+ For a [cooking website][3] I recently worked on,
2
+ I needed to display decimal floating point numbers as fractions. I needed to
3
+ come up with the fraction closest to what the user typed. For example, 0.33
4
+ should resolve to ⅓. When I googled for a solution, most of the code I found
5
+ was slow, buggy, and worst of all, too precise (returning 33/100 for the above example.)
6
+
7
+ I decided to widen my search to C, and [found][1] a piece of code on Stack Overflow
8
+ written by David Eppstein in 1993.
9
+ It uses the [theory of continued fractions][2] to approach the correct value,
10
+ but stops when the denominator reaches some value. The limitation of such an
11
+ algorithm is that we can't choose to leave out unnatural denominators
12
+
13
+ So became `fraction`: it's Eppstein's code in a Ruby gem.
14
+
15
+ Install it with `gem`:
16
+
17
+ gem install fraction
18
+
19
+ Using this gem is easy:
20
+
21
+ require 'fraction'
22
+ num, den = 0.33.fraction # num==1, den==3
23
+
24
+ You can also get the error
25
+
26
+ num,den,err = 0.33.fraction #=> [1, 3, -0.0033333333333333]
27
+
28
+ you can choose a different maximum denominator than the default value of 10:
29
+
30
+ num, den = 0.51.fraction(100) #[51, 100, 0.0]
31
+
32
+
33
+ The best part of this gem over others is the speed:
34
+
35
+ % ruby test.rb
36
+ I'm Feeling Lucky: 19.145s
37
+ 'fraction' gem: 2.090s
38
+
39
+ subtracting the time required for an empty ruby loop, we can conclude the
40
+ algorithm itself requires only ½ of a second for 1,000,000 iterations on my Mac Pro.
41
+
42
+
43
+ [1]: http://stackoverflow.com/questions/95727/how-to-convert-floats-to-human-readable-fractions
44
+ [2]: http://www.maths.surrey.ac.uk/hosted-sites/R.Knott/Fibonacci/cfINTRO.html#termdecs
45
+ [3]: http://freshslowcooking.com
data/fraction.c CHANGED
@@ -6,7 +6,7 @@
6
6
  **
7
7
  ** Made into a ruby module by Christopher Lord, Nov 2009
8
8
  **
9
- ** usage: require 'findfract'
9
+ ** usage: require 'fraction'
10
10
  **
11
11
  ** n,d,err = 0.33.fraction
12
12
  **
@@ -24,10 +24,12 @@
24
24
 
25
25
  #include "ruby.h"
26
26
  #include <stdio.h>
27
+
28
+
27
29
  VALUE method_html_form_for(VALUE self)
28
30
  {
29
31
  VALUE res = rb_str_new2("<span class='fraction'><span class='above'>");
30
- // This is an array. to make a string form, we want to
32
+ // This is an array. to make a string form, we want to
31
33
  VALUE num = rb_obj_as_string(rb_ary_entry(self, 0));
32
34
  VALUE den = rb_obj_as_string(rb_ary_entry(self, 1));
33
35
  rb_str_concat(res, rb_obj_as_string(num));
@@ -39,19 +41,23 @@ VALUE method_html_form_for(VALUE self)
39
41
  // font-family: Verdana, Arial, sans-serif; }
40
42
  // .above { vertical-align: 0.7ex; }
41
43
  // .below { vertical-align: -0.3ex; }
42
- return res;
44
+ return res;
43
45
  }
46
+
47
+
44
48
  VALUE method_string_form_for(VALUE self)
45
49
  {
46
50
  VALUE res = rb_str_new2("");
47
- // This is an array. to make a string form, we want to
51
+ // This is an array. to make a string form, we want to
48
52
  VALUE num = rb_obj_as_string(rb_ary_entry(self, 0));
49
53
  VALUE den = rb_obj_as_string(rb_ary_entry(self, 1));
50
54
  rb_str_concat(res, rb_obj_as_string(num));
51
55
  rb_str_cat2(res, "/");
52
56
  rb_str_concat(res, rb_obj_as_string(den));
53
- return res;
57
+ return res;
54
58
  }
59
+
60
+
55
61
  VALUE method_fraction_for(int argc, VALUE * argv, VALUE self)
56
62
  {
57
63
  long m11, m12,
@@ -86,10 +92,10 @@ VALUE method_fraction_for(int argc, VALUE * argv, VALUE self)
86
92
  rb_ary_store(res, 0, numer1);
87
93
  rb_ary_store(res, 1, denom1);
88
94
  rb_ary_store(res, 2, err1);
95
+ // Although the below is very cool, it's also quite slow to execute.
89
96
  // rb_define_singleton_method(res, "to_s", method_string_form_for, 0);
90
97
  // rb_define_singleton_method(res, "to_html", method_html_form_for, 0);
91
98
 
92
-
93
99
  /* We can go one more step to find another candidate:
94
100
  m11 = m11 * ai + m12;
95
101
  m21 = m21 * ai + m22;
@@ -98,8 +104,64 @@ VALUE method_fraction_for(int argc, VALUE * argv, VALUE self)
98
104
  return res;
99
105
  }
100
106
 
107
+
108
+ /// Same as above, but implements whole fraction simplification.
109
+ /// the return value is: h, n, d, e = 3.5.whole_fraction
110
+ VALUE method_whole_fraction_for(int argc, VALUE * argv, VALUE self)
111
+ {
112
+ long m11, m12,
113
+ m21, m22;
114
+ VALUE res = rb_ary_new2(4);
115
+ VALUE maxdenr;
116
+ int maxden = 10; // the default
117
+ rb_scan_args(argc, argv, "01", &maxdenr);
118
+ if (!NIL_P(maxdenr))
119
+ maxden = NUM2INT(maxdenr);
120
+ int ai;
121
+ double x = NUM2DBL(self);
122
+ double startx = x;
123
+ m11 = m22 = 1;
124
+ m12 = m21 = 0;
125
+
126
+ // loop finding terms until denom gets too big
127
+ while (m21 * ( ai = (long)x ) + m22 <= maxden) {
128
+ long t = m11 * ai + m12;
129
+ m12 = m11;
130
+ m11 = t;
131
+ t = m21 * ai + m22;
132
+ m22 = m21;
133
+ m21 = t;
134
+ if(x==(double)ai) break;
135
+ x = 1/(x - (double) ai);
136
+ if(x>(double)0x7FFFFFFF) break;
137
+ }
138
+ VALUE wholen = INT2NUM(m11 / m21);
139
+ VALUE numer1 = INT2NUM(m11 % m21);
140
+ VALUE denom1 = INT2NUM(m21);
141
+ VALUE err1 = rb_float_new(startx - ((double) m11 / (double) m21));
142
+
143
+ rb_ary_store(res, 0, wholen);
144
+ rb_ary_store(res, 1, numer1);
145
+ rb_ary_store(res, 2, denom1);
146
+ rb_ary_store(res, 3, err1);
147
+ return res;
148
+ }
149
+
150
+
101
151
  void Init_fraction() {
152
+
153
+ rb_define_method(rb_cNumeric, "to_whole_fraction", method_whole_fraction_for, -1);
154
+ rb_define_method(rb_cFloat, "to_whole_fraction", method_whole_fraction_for, -1);
155
+
156
+ rb_define_method(rb_cNumeric, "to_fraction", method_fraction_for, -1);
157
+ rb_define_method(rb_cFloat, "to_fraction", method_fraction_for, -1);
158
+
159
+ // The following are legacy support methods. they are named a bit badly
102
160
  rb_define_method(rb_cNumeric, "fraction", method_fraction_for, -1);
103
161
  rb_define_method(rb_cFloat, "fraction", method_fraction_for, -1);
162
+
163
+ rb_define_method(rb_cNumeric, "whole_fraction", method_whole_fraction_for, -1);
164
+ rb_define_method(rb_cFloat, "whole_fraction", method_whole_fraction_for, -1);
165
+
104
166
  }
105
167
 
metadata CHANGED
@@ -1,7 +1,11 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fraction
3
3
  version: !ruby/object:Gem::Version
4
- version: "0.1"
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 2
8
+ version: "0.2"
5
9
  platform: ruby
6
10
  authors:
7
11
  - Christopher Lord
@@ -11,11 +15,11 @@ autorequire:
11
15
  bindir: bin
12
16
  cert_chain: []
13
17
 
14
- date: 2009-11-27 00:00:00 -05:00
18
+ date: 2011-05-23 00:00:00 -04:00
15
19
  default_executable:
16
20
  dependencies: []
17
21
 
18
- description: Provides a "fraction" method on all ruby floats.
22
+ description: Provides "to_fraction" and to_whole_fraction methods on all ruby floats and numerics.
19
23
  email: christopherlord+fractiongem@gmail.com
20
24
  executables: []
21
25
 
@@ -25,8 +29,10 @@ extra_rdoc_files: []
25
29
 
26
30
  files:
27
31
  - fraction.c
32
+ - README.md
33
+ - extconf.rb
28
34
  has_rdoc: true
29
- homepage: http://christopher.lord.ac
35
+ homepage: https://github.com/clord/fraction
30
36
  licenses: []
31
37
 
32
38
  post_install_message:
@@ -35,23 +41,27 @@ rdoc_options: []
35
41
  require_paths:
36
42
  - lib
37
43
  required_ruby_version: !ruby/object:Gem::Requirement
44
+ none: false
38
45
  requirements:
39
46
  - - ">="
40
47
  - !ruby/object:Gem::Version
48
+ segments:
49
+ - 0
41
50
  version: "0"
42
- version:
43
51
  required_rubygems_version: !ruby/object:Gem::Requirement
52
+ none: false
44
53
  requirements:
45
54
  - - ">="
46
55
  - !ruby/object:Gem::Version
56
+ segments:
57
+ - 0
47
58
  version: "0"
48
- version:
49
59
  requirements: []
50
60
 
51
61
  rubyforge_project:
52
- rubygems_version: 1.3.5
62
+ rubygems_version: 1.3.7
53
63
  signing_key:
54
64
  specification_version: 3
55
- summary: Provides a "fraction" method on all ruby floats.
65
+ summary: Provides a "to_fraction" method on all ruby floats.
56
66
  test_files: []
57
67