fraction 0.1 → 0.2

Sign up to get free protection for your applications and to get access to all the features.
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