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.
- data/History.txt +3 -0
- data/License.txt +20 -0
- data/Manifest.txt +29 -0
- data/README.txt +211 -0
- data/Rakefile +4 -0
- data/config/hoe.rb +73 -0
- data/config/requirements.rb +17 -0
- data/lib/float-formats.rb +11 -0
- data/lib/float-formats/bytes.rb +304 -0
- data/lib/float-formats/classes.rb +1550 -0
- data/lib/float-formats/formats.rb +580 -0
- data/lib/float-formats/native.rb +169 -0
- data/lib/float-formats/version.rb +9 -0
- data/log/debug.log +0 -0
- data/script/destroy +14 -0
- data/script/destroy.cmd +1 -0
- data/script/generate +14 -0
- data/script/generate.cmd +1 -0
- data/script/txt2html +74 -0
- data/script/txt2html.cmd +1 -0
- data/setup.rb +1585 -0
- data/tasks/deployment.rake +34 -0
- data/tasks/environment.rake +7 -0
- data/tasks/website.rake +17 -0
- data/test/gen_test_data.rb +119 -0
- data/test/test_data.yaml +1716 -0
- data/test/test_float-formats.rb +112 -0
- data/test/test_helper.rb +2 -0
- data/test/test_native-float.rb +25 -0
- metadata +88 -0
data/History.txt
ADDED
data/License.txt
ADDED
@@ -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.
|
data/Manifest.txt
ADDED
@@ -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
|
data/README.txt
ADDED
@@ -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
|
+
|
data/Rakefile
ADDED
data/config/hoe.rb
ADDED
@@ -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
|