float-formats 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.
@@ -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