float-formats 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,3 @@
1
+ == 0.1.0 2007-11-04
2
+
3
+ * Initial release
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2007 Javier Goizueta
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,29 @@
1
+ History.txt
2
+ License.txt
3
+ Manifest.txt
4
+ README.txt
5
+ Rakefile
6
+ config/hoe.rb
7
+ config/requirements.rb
8
+ lib/float-formats.rb
9
+ lib/float-formats/version.rb
10
+ lib/float-formats/bytes.rb
11
+ lib/float-formats/classes.rb
12
+ lib/float-formats/formats.rb
13
+ lib/float-formats/native.rb
14
+ log/debug.log
15
+ script/destroy
16
+ script/destroy.cmd
17
+ script/generate
18
+ script/generate.cmd
19
+ script/txt2html
20
+ script/txt2html.cmd
21
+ setup.rb
22
+ tasks/deployment.rake
23
+ tasks/environment.rake
24
+ tasks/website.rake
25
+ test/test_float-formats.rb
26
+ test/test_helper.rb
27
+ test/test_data.yaml
28
+ test/gen_test_data.rb
29
+ test/test_native-float.rb
@@ -0,0 +1,211 @@
1
+ =Introduction
2
+
3
+ Float-Formats is a Ruby package with methods to handle diverse floating-point formats.
4
+ These are some of the things that can be done with it:
5
+
6
+ * Enconding and decoding numerical values in specific floating point representations.
7
+ * Conversion of floating-point data between different formats.
8
+ * Obtaining properties of floating-point formats (ranges, precision, etc.)
9
+ * Exploring and learning about floating point representations.
10
+ * Definition and testing of new floating-point formats.
11
+
12
+ =Installation
13
+
14
+ The easiest way to install Nio is using gems:
15
+
16
+ <tt> gem install --remote float-formats -y</tt>
17
+
18
+ ==Requirements
19
+
20
+ Nio[http://nio.rubyforge.org/] 0.2.0 or later is needed. This
21
+ can be installed as a gem and should be automatically
22
+ installed by the command shown above to install float-formats.
23
+
24
+
25
+ ==Downloads
26
+
27
+ The latest version of Float-Formats and its source code can be downloaded from
28
+ * http://rubyforge.org/project/showfiles.php?group_id=4684
29
+
30
+
31
+
32
+ =Predefined formats
33
+
34
+ A number of common formats are defined as constants in the FltPnt module:
35
+
36
+ ==IEEE
37
+ <b>IEEE 754 binary</b> floating point representations in little endian order:
38
+ IEEE_SINGLE, IEEE_DOUBLE, IEEE_EXTENDED, IEEE_128 and
39
+ as little endian: IEEE_S_BE, IEEE_D_BE, IEEE_X_BE, IEEE_128_BE.
40
+ Note that the standard defines extended formats with either 64 bits or precision
41
+ (IEEE_EXTENDED, IEEE_X_BE) or 112 (IEEE_128, IEEE_128_BE).
42
+
43
+ <b>IEEE 754r decimal</b> formats (using DPD): IEEE_DEC32, IEEE_DEC64 and IEEE_DEC128.
44
+
45
+ ==Legacy
46
+ Formats of historical interest, some of which are found
47
+ in file formats still in use.
48
+
49
+ <b>Mainframe/supercomputer</b> formats:
50
+ Univac 1100 (UNIVAC_SINGLE, UNIVAC_DOUBLE),
51
+ IBM 360 etc. (IBM32, IBM64 and IBM128),
52
+ CDC 6600/7600: (CDC_SINGLE, CDC_DOUBLE),
53
+ Cray-1: (CRAY).
54
+
55
+ <b>Minis</b>: PDP11 and Vaxes: (PDP11_F, PDP11_D, VAX_F, VAX_D, VAX_G and VAX_H),
56
+ HP3000: (XS256, XS256_DOUBLE),
57
+ Wang 2200: (WANG2200).
58
+
59
+ <b>Microcomputers</b> (software implementations):
60
+ Apple II: (APPLE),
61
+ Microsoft Basic, Spectrum, etc.: (XS128),
62
+ Microsoft Quickbasic: (MBF_SINGLE, MBF_DOUBLE),
63
+ Borland Pascal: (BORLAND48).
64
+
65
+ <b>Embedded systems</b>:
66
+ Formats used in the Intel 8051 by the C51 compiler:
67
+ (C51_BCD_FLOAT, C51_BCD_DOUBLE and C51_BCD_LONG_DOUBLE).
68
+
69
+
70
+ ==Calculators
71
+ Formats used in HP SATURN based calculators (RPL): (SATURN, SATURN_X),
72
+ Classic HP 10 digit calculators: (HP_CLASSIC).
73
+
74
+
75
+
76
+ =Using the pre-defined formats
77
+
78
+ require 'float-formats'
79
+ include FltPnt
80
+
81
+ The properties of the floating point formats can be queried (which can be
82
+ used for tables or reports comparing different formats):
83
+
84
+ Size in bits of the representations:
85
+ puts IEEE_SINGLE.total_bits -> 32
86
+
87
+ Numeric radix:
88
+ puts IEEE_SINGLE.radix -> 2
89
+
90
+ Digits of precision (radix-based)
91
+ puts IEEE_SINGLE.significand_digits -> 24
92
+
93
+ Minimum and maximum values of the radix-based exponent:
94
+ puts IEEE_SINGLE.radix_min_exp -> -126
95
+ puts IEEE_SINGLE.radix_max_exp -> 127
96
+
97
+ Decimal precision
98
+ puts IEEE_SINGLE.decimal_digits_stored -> 6
99
+ puts IEEE_SINGLE.decimal_digits_necessary -> 9
100
+
101
+ Minimum and maximum decimal exponents:
102
+ puts IEEE_SINGLE.decimal_min_exp -> -37
103
+ puts IEEE_SINGLE.decimal_max_exp -> 38
104
+
105
+ ==Encode and decode numbers
106
+
107
+ The <tt>from_</tt> methods of the floating-format classes generate a floating point value
108
+ stored in a byte string from a variety of definitions:
109
+ * <tt>from_integral_sign_significand_exponent</tt> defines the value by three integers:
110
+ the sign (0 for +, 1 for -), the significand (coefficient or mantissa)
111
+ and the exponent.
112
+ * <tt>from_fmt</tt> : converts a text numeral (with an optional Nio format specifier)
113
+ to a floating point value
114
+ * <tt>from_number</tt> : converts a numerical value
115
+ to a floating point representation.
116
+
117
+ All these methods return an object of type Value that contains the encoded value (#bytes)
118
+ and the Floating point format class (#fp_format).
119
+
120
+ File.open('binary_file.dat','wb'){|f| f.write IEEE_EXTENDED.from_fmt('0.1').bytes}
121
+
122
+ puts IEEE_EXTENDED.from_fmt('0.1').to_hex(true) -> CD CC CC CC CC CC CC CC FB 3F
123
+ puts IEEE_EXTENDED.from_number(0.1).to_hex(true) -> CD CC CC CC CC CC CC CC FB 3F
124
+ puts IEEE_EXTENDED.from_integral_sign_significand_exponent(0,123,-2).to_hex(true) -> 00 00 00 00 00 00 00 F6 03 40
125
+ puts IEEE_DEC32.from_fmt('1.234').to_hex(true) -> 22 20 05 34
126
+
127
+ A floating-point encoded value can be converted to useful formats wit the to_ methods:
128
+ * <tt>to_integral_sign_significand_exponent</tt>
129
+ * <tt>to_fmt</tt>
130
+ * <tt>to_number</tt>
131
+
132
+ puts IEEE_EXTENDED.to_number(File.read('binary_file.dat'))
133
+ v = IEEE_EXTENDED.from_fmt('0.1')
134
+ puts v.to_integral_sign_significand_exponent.inspect
135
+ puts v.to_fmt
136
+ puts v.to_number(Float)
137
+
138
+ ==Special values:
139
+
140
+ Let's show the decimal expression of some interesting values using
141
+ 3 significative digits:
142
+
143
+ fmt = Nio::Fmt.mode(:gen,3)
144
+
145
+ puts IEEE_SINGLE.min_value.to_fmt(fmt) -> 1.4E-45
146
+ puts IEEE_SINGLE.min_normalized_value.to_fmt(fmt) -> 1.18E-38
147
+ puts IEEE_SINGLE.max_value.to_fmt(fmt) -> 3.4E38
148
+ puts IEEE_SINGLE.epsilon.to_fmt(fmt) -> 1.19E-7
149
+
150
+ ==Convert between formats
151
+
152
+ v = IEEE_EXTENDED.from_fmt('1.1')
153
+ v = v.convert_to(IEEE_SINGLE)
154
+ v = v.convert_to(IEEE_DEC64)
155
+
156
+
157
+ =Tools for the native floating point format
158
+ This is an optional module (must be loaded explicitely because
159
+ is somewhat intrusive; it adds methods to Float)
160
+ that useful to explore or manipulate the native Float format.
161
+
162
+ require 'float-formats/native'
163
+ include FltPnt
164
+
165
+ puts float_shortest_dec(1.0.next) -> 1.0000000000000002
166
+ puts float_dec(1.0.prev) -> 0.99999999999999988897769753748434595763683319091796875
167
+ puts float_dec(1.0.next) -> 1.0000000000000002220446049250313080847263336181640625
168
+ puts float_dec(1.0.prev) -> 0.99999999999999988897769753748434595763683319091796875
169
+ puts float_bin(1.0.next) -> 1.0000000000000000000000000000000000000000000000000001E0
170
+ puts 1.0.next-1 == Float::EPSILON -> true
171
+ puts float_significant_dec(Float::MIN_D) -> 5E-324
172
+ puts float_significant_dec(Float::MIN_D.next) -> 1.0E-323
173
+ puts float_significant_dec(Float::MAX_D.prev) -> 2.2250738585072004E-308
174
+ puts float_significant_dec(Float::MAX_D) -> 2.2250738585072009E-308
175
+ puts float_significant_dec(Float::MIN_N) -> 2.2250738585072014E-308
176
+
177
+
178
+ =Defining new formats
179
+
180
+ New formats are defined using one of the classes defined in float-formats/classes.rb
181
+ and passing the necessary parameters in a hash to the constructor.
182
+
183
+ For example, here we define a binary floating point 32-bits format with
184
+ 22 bits for the significand, 9 for the exponent and 1 for the sign
185
+ (these fields are allocated from least to most significant bits).
186
+ We'll use excess notation with bias 127 for the exponent, interpreting
187
+ the significand bits as a fractional number with the radix point after
188
+ the first bit, which will be hidden:
189
+ MY_FP = BinaryFormat.new(
190
+ :fields=>[:significand,22,:exponent,9,:sign,1],
191
+ :bias=>127, :bias_mode=>:normalized_significand,
192
+ :hidden_bit=>true)
193
+ Now we can encode values in this format, decode values, convet to other
194
+ formats, query it's range, etc:
195
+
196
+ puts MY_FP.from_fmt('0.1').to_bits_text(16) -> 1ee66666
197
+ puts MY_FP.max_value.to_fmt(Nio::Fmt.prec(3)) -> 7.88E115
198
+
199
+ You can look at float-formats/formats.rb to see how the built-in formats
200
+ are defined.
201
+
202
+ =License
203
+
204
+ This code is free to use under the terms of the GNU GENERAL PUBLIC LICENSE.
205
+
206
+ =Contact
207
+
208
+ Nio has been developed by Javier Goizueta (mailto:javier@goizueta.info).
209
+
210
+ You can contact me through Rubyforge:http://rubyforge.org/sendmessage.php?touser=25432
211
+
@@ -0,0 +1,4 @@
1
+ require 'config/requirements'
2
+ require 'config/hoe' # setup Hoe + all gem configuration
3
+
4
+ Dir['tasks/**/*.rake'].each { |rake| load rake }
@@ -0,0 +1,73 @@
1
+ require 'float-formats/version'
2
+
3
+ AUTHOR = 'Javier Goizueta' # can also be an array of Authors
4
+ EMAIL = "javier@goizueta.info"
5
+ DESCRIPTION = "Floating-Point Formats"
6
+ GEM_NAME = 'float-formats' # what ppl will type to install your gem
7
+ RUBYFORGE_PROJECT = 'float-formats' # The unix name for your project
8
+ HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
9
+ DOWNLOAD_PATH = "http://rubyforge.org/projects/#{RUBYFORGE_PROJECT}"
10
+
11
+ @config_file = "C:/Documents and Settings/jgoizueta/.rubyforge/user-config.yml"
12
+ @config = nil
13
+ RUBYFORGE_USERNAME = "jgoizueta"
14
+ def rubyforge_username
15
+ unless @config
16
+ begin
17
+ @config = YAML.load(File.read(File.expand_path(@config_file)))
18
+ rescue
19
+ puts <<-EOS
20
+ ERROR: No rubyforge config file found: #{@config_file}
21
+ Run 'rubyforge setup' to prepare your env for access to Rubyforge
22
+ - See http://newgem.rubyforge.org/rubyforge.html for more details
23
+ EOS
24
+ exit
25
+ end
26
+ end
27
+ RUBYFORGE_USERNAME.replace @config["username"]
28
+ end
29
+
30
+
31
+ REV = nil
32
+ # UNCOMMENT IF REQUIRED:
33
+ # REV = `svn info`.each {|line| if line =~ /^Revision:/ then k,v = line.split(': '); break v.chomp; else next; end} rescue nil
34
+ VERS = FltPnt::VERSION::STRING + (REV ? ".#{REV}" : "")
35
+ RDOC_OPTS = ['--quiet', '--title', 'float-formats documentation',
36
+ "--opname", "index.html",
37
+ "--line-numbers",
38
+ "--main", "README",
39
+ "--inline-source"]
40
+
41
+ class Hoe
42
+ def extra_deps
43
+ @extra_deps.reject! { |x| Array(x).first == 'hoe' }
44
+ @extra_deps
45
+ end
46
+ end
47
+
48
+ # Generate all the Rake tasks
49
+ # Run 'rake -T' to see list of generated tasks (from gem root directory)
50
+ hoe = Hoe.new(GEM_NAME, VERS) do |p|
51
+ p.author = AUTHOR
52
+ p.description = DESCRIPTION
53
+ p.email = EMAIL
54
+ p.summary = DESCRIPTION
55
+ p.url = HOMEPATH
56
+ p.rubyforge_name = RUBYFORGE_PROJECT if RUBYFORGE_PROJECT
57
+ p.test_globs = ["test/**/test_*.rb"]
58
+ p.clean_globs |= ['**/.*.sw?', '*.gem', '.config', '**/.DS_Store'] #An array of file patterns to delete on clean.
59
+
60
+ # == Optional
61
+ p.changes = p.paragraphs_of("History.txt", 0..1).join("\\n\\n")
62
+ p.extra_deps = [
63
+ ['nio', '>=0.2.0']
64
+ ]
65
+
66
+ #p.spec_extras = {} # A hash of extra values to set in the gemspec.
67
+
68
+ end
69
+
70
+ CHANGES = hoe.paragraphs_of('History.txt', 0..1).join("\\n\\n")
71
+ PATH = (RUBYFORGE_PROJECT == GEM_NAME) ? RUBYFORGE_PROJECT : "#{RUBYFORGE_PROJECT}/#{GEM_NAME}"
72
+ hoe.remote_rdoc_dir = File.join(PATH.gsub(/^#{RUBYFORGE_PROJECT}\/?/,''), 'rdoc')
73
+ hoe.rsync_args = '-av --delete --ignore-errors'
@@ -0,0 +1,17 @@
1
+ require 'fileutils'
2
+ include FileUtils
3
+
4
+ require 'rubygems'
5
+ %w[rake hoe newgem rubigen].each do |req_gem|
6
+ begin
7
+ require req_gem
8
+ rescue LoadError
9
+ puts "This Rakefile requires the '#{req_gem}' RubyGem."
10
+ puts "Installation: gem install #{req_gem} -y"
11
+ exit
12
+ end
13
+ end
14
+
15
+ $:.unshift(File.join(File.dirname(__FILE__), %w[.. lib]))
16
+
17
+ require 'float-formats'
@@ -0,0 +1,11 @@
1
+ $:.unshift File.dirname(__FILE__)
2
+
3
+ require 'float-formats/version'
4
+ require 'float-formats/bytes'
5
+ require 'float-formats/classes'
6
+ require 'float-formats/formats'
7
+
8
+ # FltPnt contains constants for common floating point formats.
9
+ module FltPnt
10
+
11
+ end
@@ -0,0 +1,304 @@
1
+ # Float-Formats
2
+ # Support for binary data representations
3
+
4
+ require 'nio'
5
+ require 'nio/sugar'
6
+
7
+ require 'enumerator'
8
+
9
+ module FltPnt
10
+
11
+
12
+ module_function
13
+
14
+ # return an hex representation of a byte string
15
+ def bytes_to_hex(sgl,sep_bytes=false)
16
+ hx = sgl.unpack('H*')[0].upcase
17
+ if sep_bytes
18
+ sep = ""
19
+ (0...hx.size).step(2) do |i|
20
+ sep << " " unless i==0
21
+ sep << hx[i,2]
22
+ end
23
+ hx = sep
24
+ end
25
+ hx
26
+ end
27
+
28
+ # generate a byte string from an hex representation
29
+ def hex_to_bytes(hex)
30
+ [hex.tr(' ','')].pack('H*')
31
+ end
32
+
33
+ # ===== Byte string manipulation ==========================================================
34
+
35
+ # Reverse the order of the bits in each byte.
36
+ def reverse_byte_bits(b)
37
+ b.chr.unpack('b*').pack("B*")[0]
38
+ end
39
+
40
+ # Reverse the order of the nibbles in each byte.
41
+ def reverse_byte_nibbles(v)
42
+ if v.kind_of?(String)
43
+ # ... reverse each byte
44
+ w = ""
45
+ v.each_byte do |b|
46
+ w << ((b >> 4)|((b&0xF)<<4))
47
+ end
48
+ v = w
49
+ else
50
+ # assume one byte
51
+ # from_hex(to_hex(v).reverse)
52
+ v = (v >> 4)|((v&0xF)<<4)
53
+ end
54
+ v
55
+ end
56
+ # reverse the order of bytes in 16-bit words
57
+ def reverse_byte_pairs(b)
58
+ w = ""
59
+ (0...b.size).step(2) do |i|
60
+ w << b[i+1]
61
+ w << b[i]
62
+ end
63
+ w
64
+ end
65
+
66
+ # Supported endianness modes for byte strings are:
67
+ # [<tt>:little_endian</tt>] (Intel order): least significant bytes come first.
68
+ # [<tt>:big_endian</tt>] (Network order): most significant bytes come first.
69
+ # [<tt>:little_big_endian</tt> or <tt>:middle_endian</tt>] (PDP-11 order): each pair of bytes
70
+ # (16-bit word) has the bytes in little endian order, but the words
71
+ # are stored in big endian order (we assume the number of bytes is even).
72
+ # Note that the <tt>:big_little_endian</tt> order which would logically complete the set is
73
+ # not currently supported as it has no known uses.
74
+ def convert_endianness(byte_str, from_endianness, to_endianness)
75
+ if from_endianness!=to_endianness
76
+ if ([:little_endian,:big_endian]+[from_endianness, to_endianness]).uniq.size==2
77
+ # no middle_endian order
78
+ byte_str = byte_str.reverse
79
+ else
80
+ # from or to is middle_endian
81
+ if [:middle_endian, :little_big_endian].include?(to_endianness)
82
+ # from little_big_endian
83
+ byte_str = convert_endianness(byte_str, from_endianness, :big_endian)
84
+ byte_str = reverse_byte_pairs(byte_str)
85
+ # now swap the byte pairs
86
+ else
87
+ # from little_big_endian
88
+ byte_str = reverse_byte_pairs(byte_str)
89
+ byte_str = convert_endianness(byte_str, :big_endian, to_endianness)
90
+ end
91
+ end
92
+ end
93
+ byte_str
94
+ end
95
+
96
+ # Binary data is handled here in three representations:
97
+ # as an Integer
98
+ # as a byte sequence (String) (with specific endianness)
99
+ # an an hex-string (nibble values) (with specific endianness)
100
+
101
+ # Convert a byte string to an integer
102
+ def bytes_to_int(bytes, byte_endianness=:little_endian, bits_little_endian=false)
103
+ i = 0
104
+ bytes = convert_endianness(bytes, byte_endianness, :big_endian)
105
+ bytes.each_byte do |b|
106
+ # reverse b is bits_little_endian
107
+ if bits_little_endian
108
+ b = reverse_byte_bits(b)
109
+ end
110
+ i <<= 8
111
+ i |= b
112
+ end
113
+ i
114
+ end
115
+
116
+ # Convert an integer to a byte string
117
+ def int_to_bytes(i, len=0, byte_endianness=:little_endian, bits_little_endian=false)
118
+ return nil if i<0
119
+ bytes = ""
120
+ while i>0
121
+ b = (i&0xFF)
122
+ if bits_little_endian
123
+ b = reverse_byte_bits(b)
124
+ end
125
+ #puts "i=#{i} b<<#{b}"
126
+ bytes << b
127
+ i >>= 8
128
+ end
129
+ bytes << 0 while bytes.size<len
130
+ bytes = convert_endianness(bytes, :little_endian, byte_endianness)
131
+ bytes
132
+ end
133
+
134
+
135
+ # convert a byte string to separate fixed-width bit-fields as integers
136
+ def get_bitfields(bytes,lens,byte_endianness=:little_endian, bits_little_endian=false)
137
+ fields = []
138
+ i = bytes_to_int(bytes,byte_endianness,bits_little_endian)
139
+ for len in lens
140
+ mask = (1<<len)-1
141
+ fields << (i&mask)
142
+ i >>= len
143
+ end
144
+ fields
145
+ end
146
+
147
+ # pack fixed-width bit-fields as integers into a byte string
148
+ def set_bitfields(lens,fields,byte_endianness=:little_endian, bits_little_endian=false)
149
+ i = 0
150
+ lens = lens.reverse
151
+ fields = fields.reverse
152
+
153
+ bits = 0
154
+
155
+ (0...lens.size).each do |j|
156
+ i <<= lens[j]
157
+ i |= fields[j]
158
+ bits += lens[j]
159
+ end
160
+ int_to_bytes i,(bits+7)/8,byte_endianness, bits_little_endian
161
+ end
162
+
163
+ # DPD (Densely Packed Decimal) encoding
164
+
165
+ # The bcd2dpd and dpd2bcd methods are adapted from Mike Cowlishaw's Rexx program:
166
+ # (mfc 2000.10.03; Rexx version with new equations 2007.02.01)
167
+ # available at http://www2.hursley.ibm.com/decimal/DPDecimal.html
168
+
169
+ # Negate a bit. Auxiliar method for DPD conversions
170
+ def bitnot(b)
171
+ (~b)&1
172
+ end
173
+
174
+ # Compress BCD to Densely Packed Decimal
175
+ #
176
+ # adapted from Mike Cowlishaw's Rexx program:
177
+ # http://www2.hursley.ibm.com/decimal/DPDecimal.html
178
+ def bcd2dpd(arg)
179
+ # assign each bit to a variable, named as in the description
180
+ a,b,c,d,e,f,g,h,i,j,k,m = ("%012B"%arg).split('').collect{|bit| bit.to_i}
181
+
182
+ # derive the result bits, using boolean expressions only
183
+ #-- [the operators are: '&'=AND, '|'=OR, '\'=NOT.]
184
+ p=b | (a & j) | (a & f & i)
185
+ q=c | (a & k) | (a & g & i)
186
+ r=d
187
+ s=(f & (bitnot(a) | bitnot(i))) | (bitnot(a) & e & j) | (e & i)
188
+ t=g | (bitnot(a) & e &k) | (a & i)
189
+ u=h
190
+ v=a | e | i
191
+ w=a | (e & i) | (bitnot(e) & j)
192
+ x=e | (a & i) | (bitnot(a) & k)
193
+ y=m
194
+
195
+ # concatenate the bits and return
196
+ # result = [p,q,r,s,t,u,v,w,x,y].collect{|bit| bit.to_s}.inject{|aa,bb|aa+bb}.to_i(2)
197
+ result = 0
198
+ [p,q,r,s,t,u,v,w,x,y].each do |bit|
199
+ result <<= 1
200
+ result |= bit
201
+ end
202
+ result
203
+ end
204
+
205
+ # Expand Densely Packed Decimal to BCD
206
+ #
207
+ # adapted from Mike Cowlishaw's Rexx program:
208
+ # http://www2.hursley.ibm.com/decimal/DPDecimal.html
209
+ def dpd2bcd(arg)
210
+
211
+ # assign each bit to a variable, named as in the description
212
+ p,q,r,s,t,u,v,w,x,y = ("%010B"%arg).split('').collect{|bit| bit.to_i}
213
+
214
+ # derive the result bits, using boolean expressions only
215
+ a= (v & w) & (bitnot(s) | t | bitnot(x))
216
+ b=p & (bitnot(v) | bitnot(w) | (s & bitnot(t) & x))
217
+ c=q & (bitnot(v) | bitnot(w) | (s & bitnot(t) & x))
218
+ d=r
219
+ e=v & ((bitnot(w) & x) | (bitnot(t) & x) | (s & x))
220
+ f=(s & (bitnot(v) | bitnot(x))) | (p & bitnot(s) & t & v & w & x)
221
+ g=(t & (bitnot(v) | bitnot(x))) | (q & bitnot(s) & t & w)
222
+ h=u
223
+ i=v & ((bitnot(w) & bitnot(x)) | (w & x & (s | t)))
224
+ j=(bitnot(v) & w) | (s & v & bitnot(w) & x) | (p & w & (bitnot(x) | (bitnot(s) & bitnot(t))))
225
+ k=(bitnot(v) & x) | (t & bitnot(w) & x) | (q & v & w & (bitnot(x) | (bitnot(s) & bitnot(t))))
226
+ m=y
227
+ # concatenate the bits and return
228
+ # result = [a,b,c,d,e,f,g,h,i,j,k,m].collect{|bit| bit.to_s}.inject{|aa,bb|aa+bb}.to_i(2)
229
+ result = 0
230
+ [a,b,c,d,e,f,g,h,i,j,k,m].each do |bit|
231
+ result <<= 1
232
+ result |= bit
233
+ end
234
+ result
235
+ end
236
+
237
+ # Pack a bcd digits string into DPD
238
+ def hexbcd_to_dpd(bcd, endianness=:big_endian)
239
+
240
+ n = bcd.size
241
+ dpd = 0
242
+ dpd_bits = 0
243
+
244
+ i = 0
245
+ m = n%3
246
+ if m>0
247
+ v = bcd2dpd(bcd[0,m].to_i(16))
248
+ i += m
249
+ n -= m
250
+ bits = m==1 ? 4 : 7
251
+ dpd_bits += bits
252
+ dpd <<= bits
253
+ dpd |= v
254
+ end
255
+
256
+ while n>0
257
+ v = bcd2dpd(bcd[i,3].to_i(16))
258
+ i += 3
259
+ n -= 3
260
+ bits = 10
261
+ dpd_bits += bits
262
+ dpd <<= bits
263
+ dpd |= v
264
+ end
265
+
266
+ [dpd, dpd_bits]
267
+ end
268
+
269
+ # Unpack DPD digits
270
+ def dpd_to_hexbcd(dpd, dpd_bits, endianness=:big_endian)
271
+
272
+ bcd = ""
273
+
274
+ while dpd_bits>=10
275
+ v = dpd2bcd(dpd & 0x3FF)
276
+ dpd >>= 10
277
+ dpd_bits -= 10
278
+ bcd = ("%03X"%v)+bcd
279
+ end
280
+
281
+ if dpd_bits>0
282
+ case dpd_bits
283
+ when 4
284
+ v = dpd & 0xF
285
+ n = 1
286
+ when 7
287
+ v = dpd & 0x7F
288
+ n = 2
289
+ else
290
+ raise "Invalid DPD data"
291
+ end
292
+ v = dpd2bcd(v,true)
293
+ bcd = ("%0#{n}X"%v)+bcd
294
+ end
295
+
296
+ bcd = bcd.reverse if endianness==:little_endian
297
+
298
+ bcd
299
+
300
+ end
301
+
302
+
303
+
304
+ end