fixedpnt 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/.autotest ADDED
@@ -0,0 +1,26 @@
1
+ # -*- ruby -*-
2
+
3
+ require "autotest/restart"
4
+
5
+ Autotest.add_hook :initialize do |at|
6
+ at.testlib = "minitest/autorun"
7
+ at.add_exception "tmp"
8
+
9
+ # at.extra_files << "../some/external/dependency.rb"
10
+ #
11
+ # at.libs << ":../some/external"
12
+ #
13
+ # at.add_exception "vendor"
14
+ #
15
+ # at.add_mapping(/dependency.rb/) do |f, _|
16
+ # at.files_matching(/test_.*rb$/)
17
+ # end
18
+ #
19
+ # %w(TestA TestB).each do |klass|
20
+ # at.extra_class_map[klass] = "test/test_misc.rb"
21
+ # end
22
+ end
23
+
24
+ # Autotest.add_hook :run_command do |at|
25
+ # system "rake build"
26
+ # end
data/.gemtest ADDED
File without changes
data/CONTRIBUTORS ADDED
@@ -0,0 +1,16 @@
1
+ This code is deeply inspired by:
2
+ * Phil Tomson: fixedpt.rb: http://rubyforge.org/projects/fixedpt/
3
+
4
+ Thank you to Brian Candler for giving me very helpfull tips:
5
+ http://www.ruby-forum.com/topic/4408936#new
6
+
7
+ Joel VanderWerf showed me an interesting way of using "[]=" instead of ".assigne":
8
+ http://www.ruby-forum.com/topic/4412205#new
9
+
10
+ Adam Prescott and Grant Olson helped me finding a way through the license-jungle:
11
+ http://www.ruby-forum.com/topic/4412205#new
12
+
13
+ Last but not least: Ryan Davis teached me how to make all that gemmable.
14
+
15
+
16
+ Any contributions will be assumed to be under the same terms as this library.
data/History.txt ADDED
@@ -0,0 +1,12 @@
1
+
2
+
3
+ === 0.0.2 / 2013-03-24
4
+
5
+ * alias :[]= :assign
6
+ * Changed license to MIT
7
+ * Added CONTRIBUTORS file
8
+
9
+ === 0.0.1 / 2013-03-13
10
+
11
+ * First release.
12
+
data/LICENSE ADDED
@@ -0,0 +1,9 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2013 Axel Friedrich and contributors (see the CONTRIBUTORS file)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
+
7
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Manifest.txt ADDED
@@ -0,0 +1,9 @@
1
+ .autotest
2
+ CONTRIBUTORS
3
+ History.txt
4
+ LICENSE
5
+ Manifest.txt
6
+ README.txt
7
+ Rakefile
8
+ lib/fixedpnt.rb
9
+ test/test_fixedpnt.rb
data/README.txt ADDED
@@ -0,0 +1,166 @@
1
+ = fixedpnt
2
+
3
+ == DESCRIPTION
4
+
5
+ Binary Fixed Point calculations with Ruby.
6
+
7
+ fixedpnt allows simulating binary fixed point calculations done in hardware.
8
+
9
+ If you want to know what fixed point numbers are, you can read this:
10
+ http://en.wikipedia.org/wiki/Fixed-point_arithmetic
11
+
12
+ There is another library from Phil Tomson which serves the same purpose
13
+ but with different properties:
14
+ http://rubyforge.org/projects/fixedpt/
15
+
16
+ For _decimal_ fixed point calculations there is at least the library
17
+ from Karl Brodowsky:
18
+ http://rubyforge.org/projects/long-decimal/
19
+
20
+
21
+ == FEATURES/PROBLEMS:
22
+
23
+ GOALS:
24
+ * Simulating fixed point calculations done in hardware
25
+ * Relatively fast (pure Ruby, for I don't know C...)
26
+ * The fixed point format of the result of an expression shall be the same as
27
+ with Matlab. Example: Lets x and y each be 4 bits long with the binary point
28
+ at one place from right. With z = x * y, z will have a length of 8 bits
29
+ with the binary point at two places from right.
30
+
31
+ IT HAS:
32
+ * tracking (at the very end you can read the min and max value a variable was assigned to)
33
+ * "Inheritance" of fixed-point format ("right" format will be calculated automatically);
34
+ * Overflow check;
35
+
36
+ IT HAS _NOT_:
37
+ * OVERFLOW: NO overflow handler; instead: raises error;
38
+ * NO COERCE;
39
+ * Syntax _not_ Matlab-compatible;
40
+ * no rounding;
41
+ * SIGNED only.
42
+ * As of now, NO DIVISION (I did not need it)
43
+
44
+ KNOWN ISSUES:
45
+ * Exception raising on overflow isn't well done.
46
+
47
+
48
+ == SYNOPSIS:
49
+
50
+ EXAMPLE USAGE:
51
+ require 'fixedpnt'
52
+ include FixedPntModule
53
+ $fixedpnt_track_min_max = true
54
+
55
+ a = fp(64, 8)
56
+ b = fp(64, 8)
57
+ c = fp
58
+
59
+ 10000.times do |i|
60
+ a.assign( i )
61
+ b.assign( 2.2 * i )
62
+ if i > 5000
63
+ c.is a - b
64
+ else
65
+ c.is a + b
66
+ end
67
+ end
68
+
69
+ puts "a.abs_min_max = " + a.abs_min_max.inspect #=> [0.0, 99999.0]
70
+ puts "b.abs_min_max = " + b.abs_min_max.inspect #=> [0.0, 219997.796875]
71
+ puts "c.abs_min_max = " + c.abs_min_max.inspect #=> [-119998.796875, 160000.0]
72
+ puts "c.format = " + c.format.inspect #=> [65, 57, 8]
73
+ p required_fp_format(0.001, 220000) #=> [30, 10]
74
+
75
+
76
+ GENERAL USAGE:
77
+
78
+ CREATION:
79
+ FixedPnt.new(total_bits=nil, frac_width=nil, value=nil)
80
+
81
+ fp1 = FixedPnt.new(32,16, 1.234)
82
+ * Sets fixed-point format to:
83
+ ** total number of bits, including sign = 32;
84
+ ** number of fractional bits = 16;
85
+ (Total number of bits may be smaller than number of fractional bits.)
86
+ * Sets value to 1.234;
87
+ fp1 = FixedPnt.new(32,16)
88
+ * Sets fixed-point format;
89
+ * Value is assigned later;
90
+ fp1 = FixedPnt.new()
91
+ * Fixed-point format and value are assigned later;
92
+ * Usefull for format-"inheritance"
93
+ * Usefull for min-max-tracking
94
+
95
+ SHORTCUT to FixedPnt.new(...):
96
+ include FixedPntModule
97
+ fp(...)
98
+
99
+ ASSIGNMENT ( # # # IMPORTANT !! # # # )
100
+
101
+ "fp1 = ...":
102
+ Use this in rare cases only!
103
+ ("=": Assignment of variable name to object.)
104
+
105
+ instead, use (for speed and tracking):
106
+
107
+ "fp2.is(fp1)":
108
+ Sets value (and format) of fp2 to equal fp1.
109
+ fp2 and fp1 must have same format, OR:
110
+ fp2's format is automatically taken from fp1
111
+ ("inherited"), OR raises error if formats are different.
112
+ "fp3.is(fp1 + fp2"):
113
+ fp3 and "fp1 + fp2" must have same format, OR:
114
+ fp3's format is automatically taken from "fp1 + fp2"
115
+ ("inherited"), OR raises error if formats are different.
116
+ "fp1.assign( a_float)" or "fp1.assign( an_integer )":
117
+ Assignes value to fp1.
118
+ "fp2.fit(fp1)":
119
+ Use this to reformat ("resize") fp1.
120
+
121
+ MEMORIZE:
122
+ "is": left and right side are FixedPnt, formats are equal or automtically set;
123
+ "fit": left and right side are FixedPnt, formats are unequal;
124
+ "assign": left is FixedPnt, right is Numeric;
125
+
126
+ MIN-MAX-TRACKING:
127
+ Stores min and max value ever assigned to this fixed-point instance.
128
+ Tracking can be disabled by setting:
129
+ $fixedpnt_track_min_max = false ;
130
+
131
+
132
+ == REQUIREMENTS:
133
+
134
+ * Ruby 1.8.7 or higher
135
+
136
+ (I used it with Ruby 1.8.7 and 1.9.3.)
137
+
138
+
139
+ == INSTALL:
140
+
141
+ * sudo gem install fixedpnt
142
+
143
+ == LICENSE:
144
+
145
+ (The MIT License)
146
+
147
+ Copyright (c) 2013 Axel Friedrich and contributors (see the CONTRIBUTORS file)
148
+
149
+ Permission is hereby granted, free of charge, to any person obtaining
150
+ a copy of this software and associated documentation files (the
151
+ 'Software'), to deal in the Software without restriction, including
152
+ without limitation the rights to use, copy, modify, merge, publish,
153
+ distribute, sublicense, and/or sell copies of the Software, and to
154
+ permit persons to whom the Software is furnished to do so, subject to
155
+ the following conditions:
156
+
157
+ The above copyright notice and this permission notice shall be
158
+ included in all copies or substantial portions of the Software.
159
+
160
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
161
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
162
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
163
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
164
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
165
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
166
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,29 @@
1
+ # -*- ruby -*-
2
+
3
+ require "rubygems"
4
+ require "hoe"
5
+
6
+ Hoe.plugin :isolate
7
+ Hoe.plugin :seattlerb
8
+
9
+ # Hoe.plugin :compiler
10
+ # Hoe.plugin :doofus
11
+ # Hoe.plugin :email
12
+ # Hoe.plugin :gem_prelude_sucks
13
+ # Hoe.plugin :git
14
+ # Hoe.plugin :history
15
+ # Hoe.plugin :inline
16
+ # Hoe.plugin :isolate
17
+ # Hoe.plugin :minitest
18
+ # Hoe.plugin :perforce
19
+ # Hoe.plugin :racc
20
+ # Hoe.plugin :rcov
21
+ # Hoe.plugin :rubyforge
22
+ # Hoe.plugin :seattlerb
23
+
24
+ Hoe.spec "fixedpnt" do
25
+ developer "Axel Friedrich", "axel dot friedrich underscore smail at gmx dot de"
26
+ license "MIT"
27
+ end
28
+
29
+ # vim: syntax=ruby
data/lib/fixedpnt.rb ADDED
@@ -0,0 +1,625 @@
1
+ # encoding: windows-1252 :encoding=Windows-1252:
2
+
3
+ $fixedpnt_track_min_max ||= false
4
+
5
+ ##########################################################################
6
+ ##########################################################################
7
+ # == DESCRIPTION
8
+ #
9
+ # Binary Fixed Point calculations with Ruby.
10
+ #
11
+ # This code is deeply inspired by:
12
+ # * Phil Tomson: fixedpt.rb: http://rubyforge.org/projects/fixedpt/
13
+ # Thank you to Brian Candler for giving me very helpfull tips:
14
+ # http://www.ruby-forum.com/topic/4408936#new
15
+ # If you want to know what fixed point numbers are, you can read this:
16
+ # http://en.wikipedia.org/wiki/Fixed-point_arithmetic
17
+ #
18
+ #
19
+ # == FEATURES/PROBLEMS:
20
+ #
21
+ # GOALS:
22
+ # * Simulating fixed point calculations done in hardware
23
+ # * Relatively fast (pure Ruby, for I don't know C...)
24
+ # * The fixed point format of the result of an expression shall be the same as
25
+ # with Matlab. Example: Lets x and y each be 4 bits long with the binary point
26
+ # at one place from right. With z = x * y, z will have a length of 8 bits
27
+ # with the binary point at two places from right.
28
+ #
29
+ # IT HAS:
30
+ # * tracking (at the very end you can read the min and max value a variable was assigned to)
31
+ # * "Inheritance" of fixed-point format ("right" format will be calculated automatically);
32
+ # * Overflow check;
33
+ #
34
+ # IT HAS _NOT_:
35
+ # * OVERFLOW: NO overflow handler; instead: raises error;
36
+ # * NO COERCE;
37
+ # * Syntax _not_ Matlab-compatible;
38
+ # * no rounding;
39
+ # * SIGNED only.
40
+ # * As of now, NO DIVISION (I did not need it)
41
+ #
42
+ # KNOWN ISSUES:
43
+ # * Exception raising on overflow isn't well done.
44
+ #
45
+ #
46
+ # == SYNOPSIS:
47
+ #
48
+ # EXAMPLE USAGE:
49
+ # require 'fixedpnt'
50
+ # include FixedPntModule
51
+ # $fixedpnt_track_min_max = true
52
+ #
53
+ # a = fp(64, 8)
54
+ # b = fp(64, 8)
55
+ # c = fp
56
+ #
57
+ # 10000.times do |i|
58
+ # a.assign( i )
59
+ # b.assign( 2.2 * i )
60
+ # if i > 5000
61
+ # c.is a - b
62
+ # else
63
+ # c.is a + b
64
+ # end
65
+ # end
66
+ #
67
+ # puts "a.abs_min_max = " + a.abs_min_max.inspect #=> [0.0, 99999.0]
68
+ # puts "b.abs_min_max = " + b.abs_min_max.inspect #=> [0.0, 219997.796875]
69
+ # puts "c.abs_min_max = " + c.abs_min_max.inspect #=> [-119998.796875, 160000.0]
70
+ # puts "c.format = " + c.format.inspect #=> [65, 57, 8]
71
+ # p required_fp_format(0.001, 220000) #=> [30, 10]
72
+ #
73
+ #
74
+ # GENERAL USAGE:
75
+ #
76
+ # CREATION:
77
+ # FixedPnt.new(total_bits=nil, frac_width=nil, value=nil)
78
+ #
79
+ # fp1 = FixedPnt.new(32,16, 1.234)
80
+ # * Sets fixed-point format to:
81
+ # ** total number of bits, including sign = 32;
82
+ # ** number of fractional bits = 16;
83
+ # (Total number of bits may be smaller than number of fractional bits.)
84
+ # * Sets value to 1.234;
85
+ # fp1 = FixedPnt.new(32,16)
86
+ # * Sets fixed-point format;
87
+ # * Value is assigned later;
88
+ # fp1 = FixedPnt.new()
89
+ # * Fixed-point format and value are assigned later;
90
+ # * Usefull for format-"inheritance"
91
+ # * Usefull for min-max-tracking
92
+ #
93
+ # SHORTCUT to FixedPnt.new(...):
94
+ # include FixedPntModule
95
+ # fp(...)
96
+ #
97
+ # ASSIGNMENT ( # # # IMPORTANT !! # # # )
98
+ #
99
+ # "fp1 = ...":
100
+ # Use this in rare cases only!
101
+ # ("=": Assignment of variable name to object.)
102
+ #
103
+ # instead, use (for speed and tracking):
104
+ #
105
+ # "fp2.is(fp1)":
106
+ # Sets value (and format) of fp2 to equal fp1.
107
+ # fp2 and fp1 must have same format, OR:
108
+ # fp2's format is automatically taken from fp1
109
+ # ("inherited"), OR raises error if formats are different.
110
+ # "fp3.is(fp1 + fp2"):
111
+ # fp3 and "fp1 + fp2" must have same format, OR:
112
+ # fp3's format is automatically taken from "fp1 + fp2"
113
+ # ("inherited"), OR raises error if formats are different.
114
+ # "fp1.assign( a_float)" or "fp1.assign( an_integer )":
115
+ # Assignes value to fp1.
116
+ # "fp2.fit(fp1)":
117
+ # Use this to reformat ("resize") fp1.
118
+ #
119
+ # MEMORIZE:
120
+ # "is": left and right side are FixedPnt, formats are equal or automtically set;
121
+ # "fit": left and right side are FixedPnt, formats are unequal;
122
+ # "assign": left is FixedPnt, right is Numeric;
123
+ #
124
+ # MIN-MAX-TRACKING:
125
+ # Stores min and max value ever assigned to this fixed-point instance.
126
+ # Tracking can be disabled by setting:
127
+ # $fixedpnt_track_min_max = false ;
128
+ #
129
+ #
130
+ # == REQUIREMENTS:
131
+ #
132
+ # * Ruby 1.8.7 or higher
133
+ #
134
+ # (I used it with Ruby 1.8.7 and 1.9.3.)
135
+ #
136
+ #
137
+ # == INSTALL:
138
+ #
139
+ # * sudo gem install fixedpnt
140
+ #
141
+ # == LICENSE:
142
+ #
143
+ # (The MIT License)
144
+ #
145
+ # Copyright (c) 2013 Axel Friedrich and contributors (see the CONTRIBUTORS file)
146
+ #
147
+ # Permission is hereby granted, free of charge, to any person obtaining
148
+ # a copy of this software and associated documentation files (the
149
+ # 'Software'), to deal in the Software without restriction, including
150
+ # without limitation the rights to use, copy, modify, merge, publish,
151
+ # distribute, sublicense, and/or sell copies of the Software, and to
152
+ # permit persons to whom the Software is furnished to do so, subject to
153
+ # the following conditions:
154
+ #
155
+ # The above copyright notice and this permission notice shall be
156
+ # included in all copies or substantial portions of the Software.
157
+ #
158
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
159
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
160
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
161
+ # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
162
+ # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
163
+ # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
164
+ # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
165
+ #
166
+ # FOR INFORMATION:
167
+ #
168
+ # Privat methods:
169
+ # overflow?:
170
+ # checks for overflow;
171
+ # equal_formats?:
172
+ # checks for equal fixed-point formats (not an explicit method);
173
+ # track_min_max__:
174
+ # stores min and max value ever assigned to this fixed-point instance;
175
+ # limits!:
176
+ # sets the upper and lower assignable value for this fixed-point instance;
177
+ #
178
+ #|_. method |_. overflow? |_. equal_formats? |_. track_min_max__ |_. limits! |_. Note |
179
+ #| new() | N(o) | N | N | N | (1) |
180
+ #| new(tw,fw) | N(o) | N | N | N | (1) |
181
+ #| new(tw,fw,val) | Y(es) | N | Y | Y | (1) |
182
+ #| .assign | Y | N | Y | Y (once) | |
183
+ #| .is | N |Y or assign format | Y | N | |
184
+ #| = | N | N | N | N |assignment to varname |
185
+ #| | | | | | |
186
+ #
187
+ # (1): "new" sets:
188
+ # * @stored_int = nil, if no value given;
189
+ # * @bits, @frac_width, @int_width if FixedPnt.new(fixnum, fixnum)
190
+ # * @bits=@frac_width=@int_width=nil if FixedPnt.new(nil, nil) # for format-inheritance
191
+ #
192
+ class FixedPnt
193
+ VERSION = "0.0.2"
194
+
195
+ attr_reader :bits, :int_width, :frac_width
196
+ attr_accessor :stored_int, :min_assigned_int, :max_assigned_int
197
+
198
+ ##################################################################
199
+ # "a = FixedPnt.new" returns fixed-point object with unset format. This is
200
+ # usefull for automatic assigning format; example:
201
+ # a = FixedPnt.new
202
+ # b = FixedPnt.new(16,8, 1.23)
203
+ # a = b + b # a's format will be set automatically
204
+ #
205
+ def initialize(total_bits=nil, frac_width=nil, value=nil )
206
+ @bits = total_bits # total number of bits
207
+ @frac_width = frac_width
208
+
209
+ @stored_int = nil
210
+ @max_stored_int = nil # max allowed value of the stored integer for the given format.
211
+ @min_stored_int = nil # min allowed value of the stored integer for the given format.
212
+ @int_width = @bits - @frac_width if @frac_width # Width of the integer part ("part left of binary point") of the stored integer incl. 1 Bit for sign
213
+ @min_assigned_int = nil # min value (as stored integer), which was tried to be assigned to this fixed-point. TODO: Replace 999999999.
214
+ @max_assigned_int = nil # max value (as stored integer), which was tried to be assigned to this fixed-point. TODO: Replace 999999999.
215
+
216
+ self.assign(value) if value
217
+ end # initialize
218
+
219
+ ##################################################################
220
+ # Assign a Float or Integer value
221
+ def assign( assigned_value )
222
+ @assigned_value = assigned_value
223
+
224
+ # TODO: Improve speed?
225
+ case @assigned_value
226
+ when Float
227
+ limits! unless @max_stored_int
228
+ @stored_int = (@assigned_value * 2**@frac_width).to_i
229
+ overflow?
230
+ when Fixnum
231
+ limits! unless @max_stored_int
232
+ @stored_int = @assigned_value << @frac_width
233
+ overflow?
234
+ else
235
+ raise
236
+ end
237
+
238
+ track_min_max__ if $fixedpnt_track_min_max
239
+
240
+ self
241
+ end # assign
242
+ alias :[]= :assign
243
+
244
+ ##################################################################
245
+ # Convert fixed_point into another fixed-point format ("resize").
246
+ def fit( fixed_point )
247
+ @assigned_value = fixed_point
248
+
249
+ limits! unless @max_stored_int
250
+ @stored_int = @assigned_value.stored_int << (@frac_width - @assigned_value.frac_width )
251
+ overflow? ## if @int_width < @assigned_value.int_width
252
+
253
+ track_min_max__ if $fixedpnt_track_min_max
254
+
255
+ self
256
+ end # fit
257
+
258
+
259
+ ##################################################################
260
+ # other: fixed_point of same format
261
+ # Assignes other to self.
262
+ # Advantage over a simple "=": tracking possible
263
+ def is( other )
264
+ @assigned_value = other
265
+
266
+ unless @frac_width # TODO: Using Nil-class and mutate class possible? (-> a.=_... with a == nil )
267
+ @bits = other.bits
268
+ @int_width = other.int_width
269
+ @frac_width = other.frac_width
270
+ else
271
+ if @bits != other.bits || @frac_width != other.frac_width
272
+ raise "!!!! ERROR: Fixed-point formats must equal, but self.format=#{ self.format.inspect } and other.format = #{ other.format.inspect }!"
273
+ end
274
+ end
275
+
276
+ @stored_int = other.stored_int
277
+ track_min_max__ if $fixedpnt_track_min_max
278
+
279
+ self
280
+ end # _=
281
+
282
+
283
+ ##################################################################
284
+ # +
285
+ # int_width = [self.int_width, other.int_width].max + 1
286
+ # frac_width = [self.frac_width, other.frac_width].max
287
+ # overflow_handler = self.overflow_handler
288
+ def +(other) # c = a + b -> c.bits = [a.bits, b.bits].max + 1 (like Matlab)
289
+ # TODO: Can speed be improved?
290
+ sif = self.int_width
291
+ oif = other.int_width
292
+
293
+ if oif > sif
294
+ int_width = oif + 1
295
+ else
296
+ int_width = sif + 1
297
+ end
298
+
299
+ sfw = self.frac_width
300
+ ofw = other.frac_width
301
+
302
+ if ofw > sfw
303
+ frac_width = ofw
304
+ else
305
+ frac_width = sfw
306
+ end
307
+
308
+ # int_width = [self.int_width, other.int_width].max + 1 # slower
309
+ # frac_width = [self.frac_width, other.frac_width].max # slower
310
+ bits = int_width + frac_width
311
+
312
+ s = @stored_int
313
+ o = other.stored_int
314
+
315
+ frac_diff = @frac_width - other.frac_width
316
+
317
+ if frac_diff > 0
318
+ o = o << frac_diff
319
+ else
320
+ s = s << -frac_diff
321
+ end
322
+
323
+ res = FixedPnt.new(bits, frac_width)
324
+ res.stored_int = s + o
325
+ res
326
+ end
327
+
328
+ ##################################################################
329
+ # Unary operator "-"
330
+ def -@
331
+ if @stored_int == @min_stored_int
332
+ raise "@stored_int must not equal @min_stored_int!"
333
+ end
334
+
335
+ res = self.dup # TODO: faster way?
336
+ res.stored_int = - res.stored_int
337
+ res.min_assigned_int = 999999999
338
+ res.max_assigned_int = -999999999
339
+ ##/ res.track_min_max__ if $fixedpnt_track_min_max
340
+ res
341
+ end # -@
342
+
343
+ ##################################################################
344
+ # -
345
+ def -(other)
346
+ # TODO: Can speed be improved?
347
+ sif = self.int_width
348
+ oif = other.int_width
349
+
350
+ if oif > sif
351
+ int_width = oif + 1
352
+ else
353
+ int_width = sif + 1
354
+ end
355
+
356
+ sfw = self.frac_width
357
+ ofw = other.frac_width
358
+
359
+ if ofw > sfw
360
+ frac_width = ofw
361
+ else
362
+ frac_width = sfw
363
+ end
364
+
365
+ # int_width = [self.int_width, other.int_width].max + 1 # slower
366
+ # frac_width = [self.frac_width, other.frac_width].max # slower
367
+ bits = int_width + frac_width
368
+
369
+ s = @stored_int
370
+ o = other.stored_int
371
+
372
+ frac_diff = @frac_width - other.frac_width
373
+
374
+ if frac_diff > 0
375
+ o = o << frac_diff
376
+ else
377
+ s = s << -frac_diff
378
+ end
379
+
380
+ res = FixedPnt.new(bits, frac_width)
381
+ res.stored_int = s - o
382
+ res
383
+ end
384
+
385
+ ##################################################################
386
+ # Matlab says for signed fixed-points:
387
+ # For c = a * b:
388
+ # c.int_width = a.int_width + b.int_width # IMHO, a.int_width + b.int_width - 1 would be sufficient
389
+ # c.frac_width = a.frac_width + b.frac_width
390
+ def *(other)
391
+ int_width = @int_width + other.int_width # IMHO, a.int_width + b.int_width - 1 would be sufficient
392
+ frac_width = @frac_width + other.frac_width
393
+ bits = int_width + frac_width
394
+
395
+ res = FixedPnt.new(bits, frac_width)
396
+ res.stored_int = self.stored_int * other.stored_int
397
+ res
398
+ end
399
+
400
+ def to_i( )
401
+ res = (@stored_int >> @frac_width)
402
+ res = res + 1 if @stored_int < 0
403
+ res
404
+ end # to_i
405
+
406
+ def to_int
407
+ to_i
408
+ end
409
+
410
+ def to_f( )
411
+ # Thanks to Brian Candler.
412
+ self.stored_int * 1.0 / (1 << @frac_width)
413
+ end # to_f
414
+
415
+ ##################################################################
416
+ # Returns the stored integer of the fixed point value.
417
+ # THE FIRST BIT IS FOR SIGN!
418
+ # Returns: String; Liefert die Integer-Darstellung (ohne virtuellen Punkt)
419
+ # TODO: Improve?
420
+ def to_bin
421
+ return nil unless @stored_int
422
+
423
+ #str = sprintf("%0#{@bits}b",@ival)
424
+ str = if @stored_int < 0
425
+ sprintf("%0#{@bits}b",2**@bits + @stored_int)
426
+ else
427
+ sprintf("%0#{@bits}b",@stored_int)
428
+ end
429
+ return str
430
+ end
431
+
432
+ ##################################################################
433
+ # Returns the binary representation of the stored integer with the virtual
434
+ # binary point inserted. THE FIRST BIT IS FOR SIGN!
435
+ # TODO: Improve?
436
+ def to_binary
437
+ return nil unless @stored_int
438
+
439
+ str = self.to_bin
440
+ values = str.split('')
441
+ tmp = @bits-@frac_width
442
+ if tmp >= 0
443
+ values.insert(@bits-@frac_width,".")
444
+ else
445
+ values.insert(0, "x.#{ 'x' * tmp.abs }")
446
+ end
447
+ values.join('')
448
+ end
449
+
450
+ ##################################################################
451
+ # Returns the actual Fixed Point format as
452
+ # [total_number_of_bits, int_width_including_sign, frac_width] .
453
+ # Actually without the signed/unsigned flag
454
+ def format( )
455
+ [ @bits, @int_width, @frac_width]
456
+ end # format
457
+
458
+
459
+ def track_min_max__( ) # for internal use only!
460
+ if !@min_assigned_int || @stored_int < @min_assigned_int
461
+ @min_assigned_int = @stored_int
462
+ end
463
+
464
+ if !@max_assigned_int || @stored_int > @max_assigned_int
465
+ @max_assigned_int = @stored_int
466
+ end
467
+
468
+ ## @min_assigned_int = [@stored_int, @min_assigned_int].min # Probably slower
469
+ ## @max_assigned_int = [@stored_int, @max_assigned_int].max # Probably slower
470
+ nil
471
+ end # track_min_max__
472
+
473
+ ##################################################################
474
+ # Returns min and max assignable values as Array.
475
+ def limits( )
476
+ limits!
477
+ mi = @min_stored_int * 1.0 / (1 << @frac_width)
478
+ ma = @max_stored_int * 1.0 / (1 << @frac_width)
479
+
480
+ [ mi, ma ]
481
+ end # limits
482
+
483
+
484
+ ##################################################################
485
+ # Returns [min_assigned_value, max_assigned_value], where:
486
+ # min_assigned_value: min value, which ever was tried to be assigned to this
487
+ # fixed-point instance.
488
+ # max_assigned_value: max value, which ever was tried to be assigned to this
489
+ # fixed-point instance.
490
+ def abs_min_max( )
491
+ mi = @min_assigned_int * 1.0 / (1 << @frac_width)
492
+ ma = @max_assigned_int * 1.0 / (1 << @frac_width)
493
+
494
+ [ mi, ma ]
495
+ end # abs_min_max
496
+
497
+ ##################################################################
498
+ # Returns [min_assigned_value, max_assigned_value], where:
499
+ # min_assigned_value: min value, which ever was tried to be assigned to this
500
+ # fixed-point instance, divided by min allowed value for this fixed-point
501
+ # format.
502
+ # max_assigned_value: max value, which ever was tried to be assigned to this
503
+ # fixed-point instance, divided by max allowed value for this fixed-point
504
+ # format.
505
+ def relative_min_max( )
506
+ limits!
507
+ [ @min_assigned_int.to_f/@min_stored_int.to_f, @max_assigned_int.to_f/@max_stored_int.to_f ]
508
+ end # relative_min_max
509
+
510
+
511
+
512
+ private
513
+ def private____________________________( )
514
+ end # private____________________________
515
+
516
+ def puts_overflow_msg( )
517
+ puts "\n!!!!! ERROR: fixedpt_raise_on_overflow for @assigned_value #{ @assigned_value.inspect }"
518
+ puts "@max_stored_int is 0b#{ @max_stored_int.to_s(2) }"
519
+ puts "@min_stored_int is 0b#{ @min_stored_int.to_s(2) }"
520
+ puts "but stored_int of val would be 0b#{ @stored_int.to_s(2) }"
521
+ puts " = #{ @stored_int.to_f/@max_stored_int } * max = #{ @stored_int.to_f/@min_stored_int } * min"
522
+ puts "@assigned_value = #{ @assigned_value
523
+ }"
524
+ puts "@bits = #{ @bits }"
525
+ puts "@int_width = #{ @int_width }"
526
+ puts "@frac_width = #{ @frac_width }"
527
+ puts "@stored_int = #{ @stored_int }"
528
+ puts "@max_stored_int = #{ @max_stored_int }"
529
+ puts "@min_stored_int = #{ @min_stored_int }"
530
+ puts "@min_assigned_int = #{ @min_assigned_int }"
531
+ puts "@max_assigned_int = #{ @max_assigned_int }"
532
+ end # puts_overflow_msg
533
+
534
+ ##################################################################
535
+ #
536
+ def overflow?( )
537
+ if @stored_int > @max_stored_int || @stored_int < @min_stored_int
538
+ puts_overflow_msg
539
+ raise "fixedpt_raise_on_overflow"
540
+ end
541
+ end
542
+
543
+ def limits!
544
+ @max_stored_int = (1 << (@bits - 1)) - 1
545
+ @min_stored_int = -@max_stored_int - 1
546
+ end
547
+ end # class FixedPnt
548
+
549
+
550
+ ##################################################################
551
+ ##################################################################
552
+ module FixedPntModule # TODO: Better way?
553
+ def fp( *args )
554
+ FixedPnt.new(*args)
555
+ end # fi
556
+
557
+ ##################################################################
558
+ # Calculates the required Fixed-Point format for given
559
+ # smallest_representable_value and biggest_overall_value.
560
+ # Returns: [required_no_of_bits, required_fractional_width]
561
+ def required_fp_format(smallest_representable_value, biggest_overall_value )
562
+ smallest_bin_place = smallest_representable_value.max_significant_bin_place # "Nachkommastelle" -> negatives Vorzeichen
563
+ biggest_bin_place = biggest_overall_value.max_significant_bin_place # "Nachkommastelle" -> negatives Vorzeichen
564
+
565
+ [(biggest_bin_place - smallest_bin_place + 2).to_i, (-smallest_bin_place).to_i]
566
+ end # required_fp_format
567
+
568
+ end # module FixedPntModule
569
+
570
+
571
+ class Numeric
572
+ ##################################################################
573
+ # Returns the max significant place of the binary representation (TODO: right
574
+ # wording?). Negative sign means that most significant binary place is right
575
+ # from the binary point.
576
+ #
577
+ # EXAMPLES:
578
+ # 33.max_significant_bin_place -> 6 (33 = 0b100001)
579
+ # (1.0/33).max_significant_bin_place -> -6 (1/33 = 0b0.000001111100001)
580
+ def max_significant_bin_place( )
581
+ x = self.to_f.abs * 1.0000001 # 1.0000001 is for getting right values like 2**3 and 2**-3 TODO: Improve
582
+
583
+ if x < 1
584
+ x = 1.0 / x
585
+ sign = -1
586
+ else
587
+ sign = 1
588
+ end
589
+
590
+ x.to_i.to_s(2).size * sign
591
+ end # max_significant_bin_place
592
+ end
593
+
594
+ ##########################################################################
595
+ ##########################################################################
596
+ if $0 == __FILE__
597
+
598
+ include FixedPntModule
599
+ $fixedpnt_track_min_max = true
600
+
601
+ puts '###################### EXAMPLE USAGE '
602
+ a = fp(64, 8)
603
+ b = fp(64, 8)
604
+ c = fp
605
+
606
+ 10000.times do |j|
607
+ a.assign( j )
608
+ b.assign( 2.2 * j )
609
+ if j > 5000
610
+ c.is a - b
611
+ else
612
+ c.is a + b
613
+ end
614
+ end
615
+
616
+ puts "a.abs_min_max = " + a.abs_min_max.inspect
617
+ puts "b.abs_min_max = " + b.abs_min_max.inspect
618
+ puts "c.abs_min_max = " + c.abs_min_max.inspect
619
+ puts "c.format = " + c.format.inspect
620
+
621
+ # Calculates the required Fixed-Point format for given
622
+ # smallest_representable_value and biggest_overall_value.
623
+ p required_fp_format(0.001, 22000)
624
+
625
+ end
@@ -0,0 +1,113 @@
1
+ require "minitest/autorun"
2
+ begin
3
+ require "fixedpnt"
4
+ rescue LoadError
5
+ $: << File.dirname(File.dirname( File.expand_path( __FILE__ ) ).gsub('\\', '/')) + '/lib'
6
+ require "fixedpnt"
7
+ end
8
+ include FixedPntModule
9
+
10
+ class TestFixedpnt < MiniTest::Unit::TestCase
11
+ def setup
12
+ $fixedpnt_track_min_max = true
13
+ end
14
+ def test_assign
15
+ f = FixedPnt.new(32,16)
16
+ f.assign( 3 )
17
+ assert_equal "0000000000000011.0000000000000000", f.to_binary
18
+ f.assign( 3.5 )
19
+ assert_equal "0000000000000011.1000000000000000", f.to_binary
20
+ f[]= -3
21
+ assert_equal "1111111111111101.0000000000000000", f.to_binary
22
+ f.assign( -3.5 )
23
+ assert_equal "1111111111111100.1000000000000000", f.to_binary
24
+ end
25
+
26
+ def test_overflow( )
27
+ f = FixedPnt.new(3,0)
28
+ f.assign( 3 )
29
+ assert_equal "011.", f.to_binary
30
+ puts "\nSorry, PLEASE IGNORE the following error message:"
31
+ assert_raises(RuntimeError) {|| f.assign( 4 ) } # TODO: Improve my exception
32
+
33
+ f = FixedPnt.new(4,1)
34
+ f.assign( 3 )
35
+ assert_equal "011.0", f.to_binary
36
+ puts "\nSorry, PLEASE IGNORE the following error message:"
37
+ assert_raises(RuntimeError) {|| f.assign( 5 ) }
38
+
39
+ f = FixedPnt.new(3,0)
40
+ f.assign( -3.5 )
41
+ assert_equal "101.", f.to_binary
42
+ puts "\nSorry, PLEASE IGNORE the following error message:"
43
+ assert_raises(RuntimeError) {|| f.assign( -5 ) }
44
+ end # test_overflow
45
+
46
+ def test_add( )
47
+ f = fp(4,0)
48
+ f.assign( 2 )
49
+
50
+ g = fp(4,0)
51
+ g.assign( 3 )
52
+
53
+ z = fp
54
+ z.is f + g
55
+ assert_equal 5.0 , z.to_f
56
+ assert_equal [5, 5, 0] , z.format
57
+ assert_raises(RuntimeError) {|| z.is g } # TODO: Improve my exception
58
+ end # test_add
59
+
60
+ def test_unary_minus( )
61
+ x = fp(4,0)
62
+ x.assign( 3 )
63
+ y = -x
64
+ assert_equal( -3.0 , y.to_f )
65
+ end # test_unary_minus
66
+
67
+ def test_substract( )
68
+ x = fp(4, 0)
69
+ y = fp(4, 0)
70
+ x.assign( 3 )
71
+ y.assign( 5 )
72
+ z = x - y
73
+ assert_equal( -2.0 , z.to_f )
74
+ assert_equal [5, 5, 0] , z.format
75
+ end # test_substract
76
+
77
+ def test_multiply( )
78
+ x = fp(4, 1)
79
+ y = fp(4, 1)
80
+ x.assign( -3 )
81
+ y.assign( 3 )
82
+ z = x * y
83
+ assert_equal( -9.0 , z.to_f )
84
+ assert_equal [8, 6, 2] , z.format
85
+ end # test_multiply
86
+
87
+ def test_to_i( )
88
+ y = -12.25
89
+ x = fp(32, 16)
90
+ x.assign( y )
91
+ assert_equal( -12.25 , x.to_f )
92
+ assert_equal( y.to_f , x.to_f )
93
+ assert_equal( -12 , x.to_i )
94
+ assert_equal( y.to_i , x.to_i )
95
+ end # test_to_i
96
+
97
+ def test_track_min_max( )
98
+ x = FixedPnt.new(32, 20)
99
+
100
+ [0.5, 0.9 , 0.1, -0.3, 0.2].each {|val|
101
+ x.assign( val )
102
+ # y = FixedPnt.new(val, 1,10,9)
103
+ # p y.to_f
104
+ # x.update(y)
105
+ }
106
+ assert_in_delta( -2048.0, x.limits[0], 1e-6 )
107
+ assert_in_delta( 2048.0, x.limits[1], 1e-6 )
108
+ assert_in_delta( -0.3, x.abs_min_max[0], 1e-6 )
109
+ assert_in_delta( 0.9, x.abs_min_max[1], 1e-6 )
110
+ assert_in_delta( 0.000146, x.relative_min_max[0], 1e-6 )
111
+ assert_in_delta( 0.000439, x.relative_min_max[1], 1e-6 )
112
+ end # test_track_min_max
113
+ end
metadata ADDED
@@ -0,0 +1,123 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fixedpnt
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 2
10
+ version: 0.0.2
11
+ platform: ruby
12
+ authors:
13
+ - Axel Friedrich
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2013-03-26 00:00:00 +01:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: rdoc
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ hash: 19
30
+ segments:
31
+ - 3
32
+ - 10
33
+ version: "3.10"
34
+ type: :development
35
+ version_requirements: *id001
36
+ - !ruby/object:Gem::Dependency
37
+ name: hoe
38
+ prerelease: false
39
+ requirement: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ~>
43
+ - !ruby/object:Gem::Version
44
+ hash: 13
45
+ segments:
46
+ - 3
47
+ - 5
48
+ version: "3.5"
49
+ type: :development
50
+ version_requirements: *id002
51
+ description: |-
52
+ Binary Fixed Point calculations with Ruby.
53
+
54
+ fixedpnt allows simulating binary fixed point calculations done in hardware.
55
+
56
+ If you want to know what fixed point numbers are, you can read this:
57
+ http://en.wikipedia.org/wiki/Fixed-point_arithmetic
58
+
59
+ There is another library from Phil Tomson which serves the same purpose
60
+ but with different properties:
61
+ http://rubyforge.org/projects/fixedpt/
62
+
63
+ For _decimal_ fixed point calculations there is at least the library
64
+ from Karl Brodowsky:
65
+ http://rubyforge.org/projects/long-decimal/
66
+ email:
67
+ - axel dot friedrich underscore smail at gmx dot de
68
+ executables: []
69
+
70
+ extensions: []
71
+
72
+ extra_rdoc_files:
73
+ - History.txt
74
+ - Manifest.txt
75
+ - README.txt
76
+ files:
77
+ - .autotest
78
+ - CONTRIBUTORS
79
+ - History.txt
80
+ - LICENSE
81
+ - Manifest.txt
82
+ - README.txt
83
+ - Rakefile
84
+ - lib/fixedpnt.rb
85
+ - test/test_fixedpnt.rb
86
+ - .gemtest
87
+ has_rdoc: true
88
+ homepage:
89
+ licenses:
90
+ - MIT
91
+ post_install_message:
92
+ rdoc_options:
93
+ - --main
94
+ - README.txt
95
+ require_paths:
96
+ - lib
97
+ required_ruby_version: !ruby/object:Gem::Requirement
98
+ none: false
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ hash: 3
103
+ segments:
104
+ - 0
105
+ version: "0"
106
+ required_rubygems_version: !ruby/object:Gem::Requirement
107
+ none: false
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ hash: 3
112
+ segments:
113
+ - 0
114
+ version: "0"
115
+ requirements: []
116
+
117
+ rubyforge_project: fixedpnt
118
+ rubygems_version: 1.5.0
119
+ signing_key:
120
+ specification_version: 3
121
+ summary: Binary Fixed Point calculations with Ruby
122
+ test_files:
123
+ - test/test_fixedpnt.rb