fixed_point 0.1.0
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.
- data/HISTORY.md +14 -0
- data/LICENSE +25 -0
- data/README.md +35 -0
- data/Rakefile +11 -0
- data/lib/fixed_point.rb +13 -0
- data/lib/fixed_point/fixdt.rb +8 -0
- data/lib/fixed_point/format.rb +55 -0
- data/lib/fixed_point/number.rb +312 -0
- data/spec/fixed_point_negative_spec.rb +52 -0
- data/spec/fixed_point_spec.rb +311 -0
- data/spec/spec_helper.rb +4 -0
- metadata +77 -0
data/HISTORY.md
ADDED
data/LICENSE
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
Copyright (c) 2011, Morgan Prior
|
|
2
|
+
All rights reserved.
|
|
3
|
+
|
|
4
|
+
Redistribution and use in source and binary forms, with or without
|
|
5
|
+
modification, are permitted provided that the following conditions are met:
|
|
6
|
+
* Redistributions of source code must retain the above copyright
|
|
7
|
+
notice, this list of conditions and the following disclaimer.
|
|
8
|
+
* Redistributions in binary form must reproduce the above copyright
|
|
9
|
+
notice, this list of conditions and the following disclaimer in the
|
|
10
|
+
documentation and/or other materials provided with the distribution.
|
|
11
|
+
* Neither the name of the organization nor the
|
|
12
|
+
names of its contributors may be used to endorse or promote products
|
|
13
|
+
derived from this software without specific prior written permission.
|
|
14
|
+
|
|
15
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
16
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
17
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
18
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
|
|
19
|
+
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
20
|
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
21
|
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
22
|
+
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
23
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
24
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
25
|
+
|
data/README.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
FixedPoint
|
|
2
|
+
============
|
|
3
|
+
|
|
4
|
+
For modeling fixed point signed and unsigned data types and having nice function for printing hex and binary forms.
|
|
5
|
+
|
|
6
|
+
Install
|
|
7
|
+
-------
|
|
8
|
+
|
|
9
|
+
gem install fixed_point
|
|
10
|
+
|
|
11
|
+
Usage
|
|
12
|
+
-----
|
|
13
|
+
|
|
14
|
+
Checkout the examples folder, but here are a few:
|
|
15
|
+
|
|
16
|
+
require 'fixed_point'
|
|
17
|
+
|
|
18
|
+
#Create fixed point format, Signed, 12 integer bits, 4 fractional bits
|
|
19
|
+
format = FixedPoint::Format.new(1, 12, 4)
|
|
20
|
+
|
|
21
|
+
#Create fixed_point with value 1024.75
|
|
22
|
+
fix_num = FixedPoint::Number.new(1024.75, format )
|
|
23
|
+
|
|
24
|
+
puts fix_num.to_f # Float
|
|
25
|
+
puts fix_num.to_h # Hexadecimal
|
|
26
|
+
puts fix_num.to_b # Binary
|
|
27
|
+
|
|
28
|
+
TODO
|
|
29
|
+
----
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
LICENSE
|
|
33
|
+
-------
|
|
34
|
+
|
|
35
|
+
See the LICENSE file
|
data/Rakefile
ADDED
data/lib/fixed_point.rb
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module FixedPoint
|
|
2
|
+
VERSION = '0.1.0'
|
|
3
|
+
end
|
|
4
|
+
|
|
5
|
+
begin
|
|
6
|
+
require_relative 'fixed_point/format'
|
|
7
|
+
require_relative 'fixed_point/fixdt'
|
|
8
|
+
require_relative 'fixed_point/number'
|
|
9
|
+
rescue
|
|
10
|
+
require 'fixed_point/format'
|
|
11
|
+
require 'fixed_point/fixdt'
|
|
12
|
+
require 'fixed_point/number'
|
|
13
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
module FixedPoint
|
|
2
|
+
|
|
3
|
+
class Format
|
|
4
|
+
|
|
5
|
+
attr_reader :signed, :int_bits, :frac_bits
|
|
6
|
+
|
|
7
|
+
# Calculated attributes
|
|
8
|
+
attr_reader :width
|
|
9
|
+
attr_reader :resolution, :max_value, :min_value
|
|
10
|
+
attr_reader :max_int_signed, :max_int_unsigned
|
|
11
|
+
attr_reader :max_frac
|
|
12
|
+
attr_reader :max_signed, :max_unsigned
|
|
13
|
+
|
|
14
|
+
def initialize(signed, int_bits, frac_bits)
|
|
15
|
+
@signed = signed
|
|
16
|
+
@int_bits = int_bits
|
|
17
|
+
@frac_bits = frac_bits
|
|
18
|
+
|
|
19
|
+
calculate_attributes(signed, int_bits, frac_bits)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def signed?
|
|
23
|
+
(@signed == 1)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
#Format Should hold the maxim possible values
|
|
27
|
+
#not part of number (Value)
|
|
28
|
+
def calculate_attributes(signed, int_bits, frac_bits)
|
|
29
|
+
@width = int_bits + frac_bits
|
|
30
|
+
|
|
31
|
+
#Calculate Number ranges
|
|
32
|
+
@resolution = 2**(-@frac_bits)
|
|
33
|
+
@max_frac = 1 - 2**(-@frac_bits)
|
|
34
|
+
@max_int_signed = ( 2**(@int_bits - 1) - 1)
|
|
35
|
+
@max_int_unsigned = ( 2**@int_bits - 1)
|
|
36
|
+
@max_signed = @max_int_signed + @max_frac
|
|
37
|
+
@max_unsigned = @max_int_unsigned + @max_frac
|
|
38
|
+
|
|
39
|
+
@min_signed = (-2**(@int_bits-1))
|
|
40
|
+
@min_unsigned = 0
|
|
41
|
+
|
|
42
|
+
#Set Max/Min values
|
|
43
|
+
if signed?
|
|
44
|
+
@max_value = @max_signed
|
|
45
|
+
@min_value = @min_signed
|
|
46
|
+
else
|
|
47
|
+
@max_value = @max_unsigned
|
|
48
|
+
@min_value = @min_unsigned
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
end #module FixedPoint
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
module FixedPoint
|
|
2
|
+
|
|
3
|
+
class Number
|
|
4
|
+
attr_reader :source #Input value
|
|
5
|
+
attr_reader :format
|
|
6
|
+
|
|
7
|
+
############################################
|
|
8
|
+
#### Note
|
|
9
|
+
## All methods which set the @source value
|
|
10
|
+
## must also set the @quantised value.
|
|
11
|
+
## Every thing else is calculated on the fly
|
|
12
|
+
## based on those 2 values
|
|
13
|
+
############################################
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
####################################
|
|
17
|
+
### Init
|
|
18
|
+
####################################
|
|
19
|
+
def initialize(number, input_format=Format.new(1,12,20), decimal_mark=".")
|
|
20
|
+
@source = number
|
|
21
|
+
@format = input_format
|
|
22
|
+
@decimal_mark = decimal_mark
|
|
23
|
+
|
|
24
|
+
@warnings = false
|
|
25
|
+
|
|
26
|
+
#Now construct values based on config data
|
|
27
|
+
@quantised = quantise_value( source )
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
####################################
|
|
31
|
+
### Number overflow/underflow flags
|
|
32
|
+
####################################
|
|
33
|
+
def overflow?
|
|
34
|
+
@overflow
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def underflow?
|
|
38
|
+
@underflow
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
####################################
|
|
42
|
+
### Methods to return number formats
|
|
43
|
+
####################################
|
|
44
|
+
def bin
|
|
45
|
+
binary
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def hex
|
|
49
|
+
hexadecimal
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
#To Binary form
|
|
53
|
+
def to_b
|
|
54
|
+
binary
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
#To Hexadecimal form
|
|
58
|
+
def to_h
|
|
59
|
+
hexadecimal
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
#To Floating Point form, quantised version of _source_
|
|
63
|
+
def to_f
|
|
64
|
+
@quantised
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
#To Integer, limited integer part of _source_
|
|
68
|
+
def to_i
|
|
69
|
+
@quantised.to_i
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Method returns fractional section of fixed point type
|
|
73
|
+
# not a to_ method as it is not representative of the whole number
|
|
74
|
+
def frac
|
|
75
|
+
(@quantised - to_i)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def fraction
|
|
79
|
+
frac
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
####################################
|
|
83
|
+
### Methods to calculate formats
|
|
84
|
+
####################################
|
|
85
|
+
def binary
|
|
86
|
+
#Take signed quantised value and create binary string
|
|
87
|
+
if (@quantised < 0) and fraction.nonzero?
|
|
88
|
+
# Fractional numbers not negative
|
|
89
|
+
# So the integer part is 1 less than other wise would be and add 1+frac
|
|
90
|
+
ret_bin_int = (@format.max_int_unsigned + to_i )
|
|
91
|
+
frac_value = 1 + fraction
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
if (@quantised < 0) and fraction.zero?
|
|
95
|
+
ret_bin_int = (@format.max_int_unsigned + to_i + 1 )
|
|
96
|
+
frac_value = fraction
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
if @quantised >= 0
|
|
100
|
+
ret_bin_int = self.to_i
|
|
101
|
+
frac_value = fraction
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
## Convert to binary String and extend to correct length
|
|
105
|
+
ret_bin_int = ret_bin_int.to_s(2).rjust(@format.int_bits, '0')
|
|
106
|
+
|
|
107
|
+
## Normalise Fractional (fractional bits shifted to appear as integer)
|
|
108
|
+
ret_bin_frac = Integer(frac_value * 2**@format.frac_bits)
|
|
109
|
+
ret_bin_frac = ret_bin_frac.to_s(2).rjust(@format.frac_bits, '0' )
|
|
110
|
+
|
|
111
|
+
#Decide if we need to add Decimal( Binary ) Point
|
|
112
|
+
if @format.frac_bits > 0
|
|
113
|
+
binary_string = ret_bin_int + @decimal_mark + ret_bin_frac
|
|
114
|
+
else
|
|
115
|
+
binary_string = ret_bin_int
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
return binary_string
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def hexadecimal
|
|
122
|
+
#Clean Binary code (remove _ - . etc)
|
|
123
|
+
clean_binary = to_b.scan(/[01]/).join('')
|
|
124
|
+
|
|
125
|
+
#Convert to unsigned int then to hex
|
|
126
|
+
hex = clean_binary.to_i(2).to_s(16)
|
|
127
|
+
hex_chars = (@format.width/4.0).ceil
|
|
128
|
+
|
|
129
|
+
## Extend to the correct length
|
|
130
|
+
## Negative numbers will already have MSBs this if for small +VE
|
|
131
|
+
return hex.rjust(hex_chars, '0')
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
####################################
|
|
135
|
+
### Methods to set value from fixed point format
|
|
136
|
+
####################################
|
|
137
|
+
def binary=(text)
|
|
138
|
+
if text.match(/([01]*)(.?)([01]*)/ )
|
|
139
|
+
set_int = $1
|
|
140
|
+
int_bits = $1.size
|
|
141
|
+
|
|
142
|
+
@decimal_mark = $2
|
|
143
|
+
set_frac = $3
|
|
144
|
+
frac_bits = $3.size
|
|
145
|
+
|
|
146
|
+
#TODO Warn if the number of bits supplied does not match @format
|
|
147
|
+
|
|
148
|
+
## This should now create a new format type
|
|
149
|
+
# Do not change the Signed format as can not detect that from bit pattern
|
|
150
|
+
@format = Format.new(@format.signed, int_bits, frac_bits)
|
|
151
|
+
|
|
152
|
+
###########################
|
|
153
|
+
### Routine to generate source from binary
|
|
154
|
+
###########################
|
|
155
|
+
@source = 0.0
|
|
156
|
+
index = 0
|
|
157
|
+
set_int.reverse.each_char do |x|
|
|
158
|
+
if x == "1"
|
|
159
|
+
#If input is signed then MSB is negative
|
|
160
|
+
if ((index + 1) == @format.int_bits) and (@format.signed?)
|
|
161
|
+
@source = @source + -2**index
|
|
162
|
+
else
|
|
163
|
+
@source = @source + 2**index
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
index = index + 1
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
index = 1
|
|
170
|
+
set_frac.each_char do |x|
|
|
171
|
+
if x == "1"
|
|
172
|
+
@source = @source + 2**-index
|
|
173
|
+
end
|
|
174
|
+
index = index + 1
|
|
175
|
+
end
|
|
176
|
+
################################
|
|
177
|
+
|
|
178
|
+
## Set the Quantised value
|
|
179
|
+
@quantised = @source
|
|
180
|
+
|
|
181
|
+
return binary
|
|
182
|
+
else
|
|
183
|
+
puts "ERROR invalid input binary\(#{text}\)"
|
|
184
|
+
return nil
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
#def hex=( text )
|
|
189
|
+
#end
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
#TODO
|
|
193
|
+
#def log
|
|
194
|
+
#def log2
|
|
195
|
+
|
|
196
|
+
def warnings( val=true )
|
|
197
|
+
@warnings = val
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
private
|
|
201
|
+
|
|
202
|
+
def check_for_overflow_underflow( source, format)
|
|
203
|
+
overflow = false
|
|
204
|
+
underflow = false
|
|
205
|
+
|
|
206
|
+
#WARN +VE
|
|
207
|
+
if (source > 0) and (source > format.max_value)
|
|
208
|
+
puts "WARNING Maximum number is #{format.max_value} input was #{source}" if @warnings
|
|
209
|
+
overflow = true
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
##WARN -VE
|
|
213
|
+
if (source < 0) and (source < format.min_value)
|
|
214
|
+
puts "WARNING Minimum number is #{format.min_value} input was #{source}" if @warnings
|
|
215
|
+
underflow = true
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
return [overflow, underflow]
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def quantise_value( source )
|
|
223
|
+
#Overflow / Underflow flags
|
|
224
|
+
@overflow, @underflow = check_for_overflow_underflow( source, @format)
|
|
225
|
+
|
|
226
|
+
## Create fractional only number
|
|
227
|
+
source_frac = source - source.to_i
|
|
228
|
+
|
|
229
|
+
#Logic for fractional negative numbers is different
|
|
230
|
+
if (( source < 0) and source_frac.nonzero? )
|
|
231
|
+
#Integer bits become 1 more negative
|
|
232
|
+
number_int = Integer( source )-1
|
|
233
|
+
|
|
234
|
+
#The @fractional part inverts so int+frac = original number
|
|
235
|
+
number_frac = ( source - number_int)
|
|
236
|
+
else
|
|
237
|
+
# Create Integer only number
|
|
238
|
+
number_int = source.to_i
|
|
239
|
+
|
|
240
|
+
#Fractional Part
|
|
241
|
+
number_frac = source_frac
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
if overflow?
|
|
246
|
+
if @format.signed?
|
|
247
|
+
number_int = @format.max_int_signed
|
|
248
|
+
number_frac = @format.max_frac
|
|
249
|
+
else
|
|
250
|
+
number_int = @format.max_int_unsigned
|
|
251
|
+
number_frac = @format.max_frac
|
|
252
|
+
end
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
if underflow?
|
|
256
|
+
if @format.signed?
|
|
257
|
+
number_int = 2**(@format.int_bits-1)
|
|
258
|
+
number_frac = 0
|
|
259
|
+
else
|
|
260
|
+
number_int = 0
|
|
261
|
+
number_frac = 0
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
##Roll the integer number over the number space so binary conversion gives correct Twos complement
|
|
266
|
+
if number_int < 0
|
|
267
|
+
number_int = @format.max_int_unsigned + (number_int + 1)
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
###################################
|
|
271
|
+
### Quantized Fractional value
|
|
272
|
+
###################################
|
|
273
|
+
# Normalise, represent as integer
|
|
274
|
+
# Fractional data is removed by the integer.
|
|
275
|
+
# We are only left with @format.frac_bits worth of data
|
|
276
|
+
number_frac_quant = (number_frac * 2**@format.frac_bits).to_i / (2**@format.frac_bits).to_f
|
|
277
|
+
|
|
278
|
+
# TODO Comment on what this is actually doing
|
|
279
|
+
# some sought of signed conversion
|
|
280
|
+
if (@source < 0) and number_frac_quant.nonzero?
|
|
281
|
+
number_int = number_int - @format.max_int_unsigned
|
|
282
|
+
number_frac_quant = number_frac_quant - 1
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
if (@source < 0) and number_frac_quant.zero?
|
|
286
|
+
number_int = number_int - @format.max_int_unsigned - 1
|
|
287
|
+
number_frac_quant = 0
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
return (number_int + number_frac_quant)
|
|
291
|
+
end ## quantise_input
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
## Taking methd out until covered by tests
|
|
298
|
+
#def normalised
|
|
299
|
+
# #This use to be only for positive numbers
|
|
300
|
+
#
|
|
301
|
+
# # This function shiftes the fixedpoint number
|
|
302
|
+
# # so it can be represented as an integer.
|
|
303
|
+
# return Integer((@quantised)*(2**@format.frac_bits))
|
|
304
|
+
#end
|
|
305
|
+
|
|
306
|
+
def FixedPoint_debug(msg)
|
|
307
|
+
if true == false
|
|
308
|
+
puts msg
|
|
309
|
+
end
|
|
310
|
+
end
|
|
311
|
+
end #class number
|
|
312
|
+
end #module FixedPoint
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe FixedPoint do
|
|
4
|
+
|
|
5
|
+
it "Negative Integers -1 " do
|
|
6
|
+
format = FixedPoint::Format.new(1,4,0)
|
|
7
|
+
fixt = FixedPoint::Number.new(-1.0, format, "_")
|
|
8
|
+
|
|
9
|
+
fixt.source.should == -1.0
|
|
10
|
+
fixt.to_f.should == -1.0
|
|
11
|
+
fixt.to_i.should == -1
|
|
12
|
+
fixt.frac.should == 0.0
|
|
13
|
+
fixt.to_h.should == "f"
|
|
14
|
+
fixt.to_b.should == "1111"
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it "Negative Integers -2 " do
|
|
18
|
+
format = FixedPoint::Format.new(1,4,0)
|
|
19
|
+
fixt = FixedPoint::Number.new(-2.0, format, "_")
|
|
20
|
+
|
|
21
|
+
fixt.source.should == -2.0
|
|
22
|
+
fixt.to_f.should == -2.0
|
|
23
|
+
fixt.to_i.should == -2
|
|
24
|
+
fixt.frac.should == 0.0
|
|
25
|
+
fixt.to_h.should == "e"
|
|
26
|
+
fixt.to_b.should == "1110"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it "Negative Integers -1.5 " do
|
|
30
|
+
format = FixedPoint::Format.new(1,4,4)
|
|
31
|
+
fixt = FixedPoint::Number.new(-1.5, format, "_")
|
|
32
|
+
|
|
33
|
+
fixt.source.should == -1.5
|
|
34
|
+
fixt.to_f.should == -1.5
|
|
35
|
+
fixt.to_i.should == -1
|
|
36
|
+
fixt.frac.should == -0.5
|
|
37
|
+
fixt.to_h.should == "e8"
|
|
38
|
+
fixt.to_b.should == "1110_1000"
|
|
39
|
+
end
|
|
40
|
+
it "Negative Integers -2.25 " do
|
|
41
|
+
format = FixedPoint::Format.new(1,4,4)
|
|
42
|
+
fixt = FixedPoint::Number.new(-2.25, format, "_")
|
|
43
|
+
|
|
44
|
+
fixt.source.should == -2.25
|
|
45
|
+
fixt.to_f.should == -2.25
|
|
46
|
+
fixt.to_i.should == -2
|
|
47
|
+
fixt.frac.should == -0.25
|
|
48
|
+
fixt.to_h.should == "dc"
|
|
49
|
+
fixt.to_b.should == "1101_1100"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
end
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
# def initialize(number, Format.new(signed=1, int_bits=12, frac_bits=20), decimal_mark=".")
|
|
4
|
+
# source Input Number
|
|
5
|
+
# to_f quantised value as float
|
|
6
|
+
# to_i quantised value as integer
|
|
7
|
+
# frac fractional part of quantised value
|
|
8
|
+
# to_h quantised value as string formatted as hex
|
|
9
|
+
# to_b quantised value as string formatted as binary
|
|
10
|
+
#
|
|
11
|
+
|
|
12
|
+
describe FixedPoint do
|
|
13
|
+
|
|
14
|
+
it "Returns 0.0 for initalise of 0" do
|
|
15
|
+
fixt = FixedPoint::Number.new(0)
|
|
16
|
+
|
|
17
|
+
fixt.source.should == 0.0
|
|
18
|
+
fixt.to_f.should == 0.0
|
|
19
|
+
fixt.to_i.should == 0
|
|
20
|
+
fixt.frac.should == 0.0
|
|
21
|
+
fixt.to_h.should == "00000000"
|
|
22
|
+
fixt.to_b.should == "000000000000.00000000000000000000"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it "Integers 0 " do
|
|
26
|
+
format = FixedPoint::Format.new(1,8,0)
|
|
27
|
+
fixt = FixedPoint::Number.new(7.0, format, "_")
|
|
28
|
+
|
|
29
|
+
fixt.source.should == 7.0
|
|
30
|
+
fixt.to_f.should == 7.0
|
|
31
|
+
fixt.to_i.should == 7
|
|
32
|
+
fixt.frac.should == 0.0
|
|
33
|
+
fixt.to_h.should == "07"
|
|
34
|
+
fixt.to_b.should == "00000111"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it "Different Decimal Mark _ instead of ." do
|
|
38
|
+
format = FixedPoint::Format.new(1,12,20)
|
|
39
|
+
fixt = FixedPoint::Number.new(0, format, "_")
|
|
40
|
+
|
|
41
|
+
fixt.source.should == 0.0
|
|
42
|
+
fixt.to_f.should == 0.0
|
|
43
|
+
fixt.to_i.should == 0
|
|
44
|
+
fixt.frac.should == 0.0
|
|
45
|
+
fixt.to_h.should == "00000000"
|
|
46
|
+
fixt.to_b.should == "000000000000_00000000000000000000"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
(1...20).to_a.reverse_each do |x|
|
|
50
|
+
int_bits = 12
|
|
51
|
+
it "Returns 0.0 for initalise of 0 with #{x} fractional bits" do
|
|
52
|
+
format = FixedPoint::Format.new(1, int_bits, x)
|
|
53
|
+
fixt = FixedPoint::Number.new(0, format)
|
|
54
|
+
#fixt = FixedPoint::Number.new(0,1,int_bits,x)
|
|
55
|
+
|
|
56
|
+
fixt.source.should == 0.0
|
|
57
|
+
fixt.to_f.should == 0.0
|
|
58
|
+
fixt.to_i.should == 0
|
|
59
|
+
fixt.frac.should == 0.0
|
|
60
|
+
#Calculate hex length and fill with 0's
|
|
61
|
+
hex = ""
|
|
62
|
+
hex_length = ((x.to_f+int_bits.to_f)/4).ceil
|
|
63
|
+
hex_length.times { hex += "0" }
|
|
64
|
+
fixt.to_h.should == hex
|
|
65
|
+
#Calculate binary fractional length and fill with 0's
|
|
66
|
+
lsbs = ""
|
|
67
|
+
x.times { lsbs += "0" }
|
|
68
|
+
fixt.to_b.should == "000000000000.#{lsbs}"
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
it "Zero fractional bits " do
|
|
74
|
+
format = FixedPoint::Format.new(1,12,0)
|
|
75
|
+
fixt = FixedPoint::Number.new(0, format, "_")
|
|
76
|
+
|
|
77
|
+
fixt.source.should == 0.0
|
|
78
|
+
fixt.to_f.should == 0.0
|
|
79
|
+
fixt.to_i.should == 0
|
|
80
|
+
fixt.frac.should == 0.0
|
|
81
|
+
fixt.to_h.should == "000"
|
|
82
|
+
fixt.to_b.should == "000000000000"
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
it "returns 2.5 for initalise of 2.5" do
|
|
87
|
+
fixt = FixedPoint::Number.new(2.5)
|
|
88
|
+
|
|
89
|
+
fixt.source.should == 2.5
|
|
90
|
+
fixt.to_f.should == 2.5
|
|
91
|
+
fixt.to_i.should == 2
|
|
92
|
+
fixt.frac.should == 0.5
|
|
93
|
+
fixt.to_h.should == "00280000"
|
|
94
|
+
fixt.to_b.should == "000000000010.10000000000000000000"
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
it "Truncates fractional numbers correctly" do
|
|
99
|
+
format = FixedPoint::Format.new(1,12,1)
|
|
100
|
+
fixt = FixedPoint::Number.new(2.501, format)
|
|
101
|
+
|
|
102
|
+
fixt.source.should == 2.501
|
|
103
|
+
fixt.to_f.should == 2.5
|
|
104
|
+
fixt.to_i.should == 2
|
|
105
|
+
fixt.frac.should == 0.5
|
|
106
|
+
fixt.to_h.should == "0005"
|
|
107
|
+
fixt.to_b.should == "000000000010.1"
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
##############################################
|
|
111
|
+
### Overflow Section
|
|
112
|
+
##############################################
|
|
113
|
+
it "Forced Overflow 4 Int Bits, 0 fractional bits " do
|
|
114
|
+
format = FixedPoint::Format.new(1, 4, 0)
|
|
115
|
+
fixt = FixedPoint::Number.new(2**3, format, "_")
|
|
116
|
+
|
|
117
|
+
fixt.source.should == 2**3
|
|
118
|
+
fixt.to_f.should == 2**3-1
|
|
119
|
+
fixt.to_i.should == 2**3-1
|
|
120
|
+
fixt.frac.should == 0.0
|
|
121
|
+
fixt.to_h.should == "7"
|
|
122
|
+
fixt.to_b.should == "0111"
|
|
123
|
+
fixt.overflow?.should == true
|
|
124
|
+
fixt.underflow?.should == false
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
it "Forced Overflow Zero fractional bits " do
|
|
128
|
+
format = FixedPoint::Format.new(1, 12, 0)
|
|
129
|
+
fixt = FixedPoint::Number.new(2**11, format, "_")
|
|
130
|
+
|
|
131
|
+
fixt.source.should == 2**11
|
|
132
|
+
fixt.to_f.should == 2**11-1
|
|
133
|
+
fixt.to_i.should == 2**11-1
|
|
134
|
+
fixt.frac.should == 0.0
|
|
135
|
+
fixt.to_h.should == "7ff"
|
|
136
|
+
fixt.to_b.should == "011111111111"
|
|
137
|
+
fixt.overflow?.should == true
|
|
138
|
+
fixt.underflow?.should == false
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
it "Forced Overflow 4 fractional bits " do
|
|
142
|
+
format = FixedPoint::Format.new(1, 12, 4)
|
|
143
|
+
fixt = FixedPoint::Number.new(2**11, format, ".")
|
|
144
|
+
|
|
145
|
+
max_fractional_value = (1.0/2) + (1.0/4) + (1.0/8) + (1.0/16)
|
|
146
|
+
|
|
147
|
+
fixt.source.should == 2**11
|
|
148
|
+
fixt.to_f.should == 2**11 -1 + max_fractional_value
|
|
149
|
+
fixt.to_i.should == 2**11 -1
|
|
150
|
+
fixt.frac.should == max_fractional_value
|
|
151
|
+
fixt.to_h.should == "7fff"
|
|
152
|
+
fixt.to_b.should == "011111111111.1111"
|
|
153
|
+
fixt.overflow?.should == true
|
|
154
|
+
fixt.underflow?.should == false
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
it "Forced Overflow Large overflow" do
|
|
158
|
+
format = FixedPoint::Format.new(1, 12, 4)
|
|
159
|
+
fixt = FixedPoint::Number.new(2**17, format, ".")
|
|
160
|
+
|
|
161
|
+
fixt.source.should == 2**17
|
|
162
|
+
|
|
163
|
+
max_fractional_value = (1.0/2) + (1.0/4) + (1.0/8) + (1.0/16)
|
|
164
|
+
|
|
165
|
+
fixt.to_f.should == 2**11 -1 + max_fractional_value
|
|
166
|
+
fixt.to_i.should == 2**11 -1
|
|
167
|
+
fixt.frac.should == max_fractional_value
|
|
168
|
+
fixt.to_h.should == "7fff"
|
|
169
|
+
fixt.to_b.should == "011111111111.1111"
|
|
170
|
+
fixt.overflow?.should == true
|
|
171
|
+
fixt.underflow?.should == false
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
##############################################
|
|
175
|
+
### Underflow Section
|
|
176
|
+
##############################################
|
|
177
|
+
#Max Negative value
|
|
178
|
+
it "Forced Overflow Zero fractional bits " do
|
|
179
|
+
format = FixedPoint::Format.new(1, 12, 0)
|
|
180
|
+
fixt = FixedPoint::Number.new(-2**11, format, "_")
|
|
181
|
+
|
|
182
|
+
fixt.source.should == -2**11
|
|
183
|
+
fixt.to_f.should == -2**11
|
|
184
|
+
fixt.to_i.should == -2**11
|
|
185
|
+
fixt.frac.should == 0.0
|
|
186
|
+
fixt.to_h.should == "800"
|
|
187
|
+
fixt.to_b.should == "100000000000"
|
|
188
|
+
fixt.overflow?.should == false
|
|
189
|
+
fixt.underflow?.should == false
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
it "Forced Overflow Zero fractional bits " do
|
|
193
|
+
format = FixedPoint::Format.new(1, 12, 0)
|
|
194
|
+
fixt = FixedPoint::Number.new(-2**11-1, format,"_")
|
|
195
|
+
|
|
196
|
+
fixt.source.should == -2**11-1
|
|
197
|
+
fixt.to_f.should == -2**11
|
|
198
|
+
fixt.to_i.should == -2**11
|
|
199
|
+
fixt.frac.should == 0.0
|
|
200
|
+
fixt.to_h.should == "800"
|
|
201
|
+
fixt.to_b.should == "100000000000"
|
|
202
|
+
fixt.overflow?.should == false
|
|
203
|
+
fixt.underflow?.should == true
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
it "Forced Overflow 4 fractional bits " do
|
|
207
|
+
format = FixedPoint::Format.new(1, 12, 4)
|
|
208
|
+
fixt = FixedPoint::Number.new(-2**11-1, format, ".")
|
|
209
|
+
|
|
210
|
+
fixt.source.should == -2**11-1
|
|
211
|
+
fixt.to_f.should == -2**11
|
|
212
|
+
fixt.to_i.should == -2**11
|
|
213
|
+
fixt.frac.should == 0.0
|
|
214
|
+
fixt.to_h.should == "8000"
|
|
215
|
+
fixt.to_b.should == "100000000000.0000"
|
|
216
|
+
fixt.overflow?.should == false
|
|
217
|
+
fixt.underflow?.should == true
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
it "Forced Overflow Large overflow" do
|
|
221
|
+
format = FixedPoint::Format.new(1, 12, 4)
|
|
222
|
+
fixt = FixedPoint::Number.new(-2**17, format, ".")
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
fixt.source.should == -2**17
|
|
226
|
+
fixt.to_f.should == -2**11
|
|
227
|
+
fixt.to_i.should == -2**11
|
|
228
|
+
fixt.frac.should == 0.0
|
|
229
|
+
fixt.to_h.should == "8000"
|
|
230
|
+
fixt.to_b.should == "100000000000.0000"
|
|
231
|
+
fixt.overflow?.should == false
|
|
232
|
+
fixt.underflow?.should == true
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
it "Creating via binary form 011_01" do
|
|
237
|
+
fixt = FixedPoint::Number.new(0)
|
|
238
|
+
|
|
239
|
+
fixt.binary = "011_01"
|
|
240
|
+
fixt.source.should == 3.25
|
|
241
|
+
fixt.to_f.should == 3.25
|
|
242
|
+
fixt.to_i.should == 3
|
|
243
|
+
fixt.frac.should == 0.25
|
|
244
|
+
fixt.to_h.should == "0d"
|
|
245
|
+
fixt.to_b.should == "011_01"
|
|
246
|
+
fixt.overflow?.should == false
|
|
247
|
+
fixt.underflow?.should == false
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
it "Creating via binary form 0_1" do
|
|
251
|
+
fixt = FixedPoint::Number.new(0)
|
|
252
|
+
|
|
253
|
+
fixt.binary = "0_1"
|
|
254
|
+
fixt.source.should == 0.5
|
|
255
|
+
fixt.to_f.should == 0.5
|
|
256
|
+
fixt.to_i.should == 0
|
|
257
|
+
fixt.frac.should == 0.5
|
|
258
|
+
fixt.to_h.should == "1"
|
|
259
|
+
fixt.to_b.should == "0_1"
|
|
260
|
+
fixt.overflow?.should == false
|
|
261
|
+
fixt.underflow?.should == false
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
it "Creating Signed via binary form 1000_1" do
|
|
265
|
+
fixt = FixedPoint::Number.new(0)
|
|
266
|
+
|
|
267
|
+
fixt.binary = "1000_1"
|
|
268
|
+
fixt.source.should == -7.5
|
|
269
|
+
fixt.to_f.should == -7.5
|
|
270
|
+
fixt.to_i.should == -7
|
|
271
|
+
fixt.frac.should == -0.5
|
|
272
|
+
fixt.to_h.should == "11"
|
|
273
|
+
fixt.to_b.should == "1000_1"
|
|
274
|
+
fixt.overflow?.should == false
|
|
275
|
+
fixt.underflow?.should == false
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
it "Creating Unsigned via binary form 1000_1" do
|
|
279
|
+
format = FixedPoint::Format.new(0, 12, 20)
|
|
280
|
+
fixt = FixedPoint::Number.new(0,format)
|
|
281
|
+
|
|
282
|
+
fixt.binary = "1000_1"
|
|
283
|
+
fixt.source.should == 8.5
|
|
284
|
+
fixt.to_f.should == 8.5
|
|
285
|
+
fixt.to_i.should == 8
|
|
286
|
+
fixt.frac.should == 0.5
|
|
287
|
+
fixt.to_h.should == "11"
|
|
288
|
+
fixt.to_b.should == "1000_1"
|
|
289
|
+
fixt.overflow?.should == false
|
|
290
|
+
fixt.underflow?.should == false
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
it "Creating Unsigned binary form 1000_1 using Fixdt datatype" do
|
|
294
|
+
format = FixedPoint::Fixdt.new(0, 32, 20) #Signed,width,frac_bits
|
|
295
|
+
fixt = FixedPoint::Number.new(0,format)
|
|
296
|
+
|
|
297
|
+
fixt.binary = "1000_1"
|
|
298
|
+
fixt.source.should == 8.5
|
|
299
|
+
fixt.to_f.should == 8.5
|
|
300
|
+
fixt.to_i.should == 8
|
|
301
|
+
fixt.frac.should == 0.5
|
|
302
|
+
fixt.to_h.should == "11"
|
|
303
|
+
fixt.to_b.should == "1000_1"
|
|
304
|
+
fixt.overflow?.should == false
|
|
305
|
+
fixt.underflow?.should == false
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
#need overflow test
|
|
309
|
+
#need underflow test
|
|
310
|
+
#Integer only binary test
|
|
311
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: fixed_point
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
hash: 27
|
|
5
|
+
prerelease:
|
|
6
|
+
segments:
|
|
7
|
+
- 0
|
|
8
|
+
- 1
|
|
9
|
+
- 0
|
|
10
|
+
version: 0.1.0
|
|
11
|
+
platform: ruby
|
|
12
|
+
authors:
|
|
13
|
+
- Morgan Prior
|
|
14
|
+
autorequire:
|
|
15
|
+
bindir: bin
|
|
16
|
+
cert_chain: []
|
|
17
|
+
|
|
18
|
+
date: 2012-01-21 00:00:00 +00:00
|
|
19
|
+
default_executable:
|
|
20
|
+
dependencies: []
|
|
21
|
+
|
|
22
|
+
description: Fixed Point numerical type for simulating fixed point calculations
|
|
23
|
+
email: fixed_point_gem@amaras-tech.co.uk
|
|
24
|
+
executables: []
|
|
25
|
+
|
|
26
|
+
extensions: []
|
|
27
|
+
|
|
28
|
+
extra_rdoc_files: []
|
|
29
|
+
|
|
30
|
+
files:
|
|
31
|
+
- LICENSE
|
|
32
|
+
- README.md
|
|
33
|
+
- HISTORY.md
|
|
34
|
+
- Rakefile
|
|
35
|
+
- lib/fixed_point/fixdt.rb
|
|
36
|
+
- lib/fixed_point/format.rb
|
|
37
|
+
- lib/fixed_point/number.rb
|
|
38
|
+
- lib/fixed_point.rb
|
|
39
|
+
- spec/fixed_point_negative_spec.rb
|
|
40
|
+
- spec/fixed_point_spec.rb
|
|
41
|
+
- spec/spec_helper.rb
|
|
42
|
+
has_rdoc: true
|
|
43
|
+
homepage: http://amaras-tech.co.uk/software/fixed_point
|
|
44
|
+
licenses: []
|
|
45
|
+
|
|
46
|
+
post_install_message:
|
|
47
|
+
rdoc_options: []
|
|
48
|
+
|
|
49
|
+
require_paths:
|
|
50
|
+
- lib
|
|
51
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
52
|
+
none: false
|
|
53
|
+
requirements:
|
|
54
|
+
- - ">="
|
|
55
|
+
- !ruby/object:Gem::Version
|
|
56
|
+
hash: 3
|
|
57
|
+
segments:
|
|
58
|
+
- 0
|
|
59
|
+
version: "0"
|
|
60
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
61
|
+
none: false
|
|
62
|
+
requirements:
|
|
63
|
+
- - ">="
|
|
64
|
+
- !ruby/object:Gem::Version
|
|
65
|
+
hash: 3
|
|
66
|
+
segments:
|
|
67
|
+
- 0
|
|
68
|
+
version: "0"
|
|
69
|
+
requirements: []
|
|
70
|
+
|
|
71
|
+
rubyforge_project:
|
|
72
|
+
rubygems_version: 1.6.2
|
|
73
|
+
signing_key:
|
|
74
|
+
specification_version: 3
|
|
75
|
+
summary: Fixed Point numerical type
|
|
76
|
+
test_files: []
|
|
77
|
+
|