float-formats 0.2.1 → 0.3.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.
- checksums.yaml +4 -4
- data/History.txt +5 -0
- data/README.md +3 -3
- data/float-formats.gemspec +5 -2
- data/lib/float-formats/bytes.rb +7 -10
- data/lib/float-formats/classes.rb +144 -100
- data/lib/float-formats/formats.rb +256 -255
- data/lib/float-formats/native.rb +9 -54
- data/lib/float-formats/version.rb +1 -1
- data/test/test_bytes.rb +44 -38
- data/test/test_data.yaml +172 -172
- data/test/test_float_formats.rb +49 -47
- data/test/test_native-float.rb +1 -1
- metadata +22 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e5c6ebefc05d57a1271453eb0ba460e7f6fa9e9d
|
4
|
+
data.tar.gz: 0bb606a66d28fa34cdf03984c72acf0ecdbb4ce3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dd8490cef4b2aacb6bb06b7fb5e349acf954055499f2bc52000c126a4095163dca2be630d7f27b60b5426e4e43772d1eae6f099423eead7ecf8b257afc59b23e
|
7
|
+
data.tar.gz: 62ce956a3757fb4719e7cf0572ff4698af641275396ba6b5a5f2d95db641e90e35148adc7499dbe0b8bccd157a682585075efab7a561cf708a4c1e9c9b6c3b7e
|
data/History.txt
CHANGED
data/README.md
CHANGED
@@ -210,9 +210,9 @@ the first bit, which will be hidden:
|
|
210
210
|
|
211
211
|
Flt.define(
|
212
212
|
:MY_FP, BinaryFormat,
|
213
|
-
:
|
214
|
-
:
|
215
|
-
:
|
213
|
+
fields: [:significand,22,:exponent,9,:sign,1],
|
214
|
+
bias: 127, bias_mode: :scientific_significand,
|
215
|
+
hidden_bit: true
|
216
216
|
)
|
217
217
|
|
218
218
|
Now we can encode values in this format, decode values, convet to other
|
data/float-formats.gemspec
CHANGED
@@ -18,9 +18,12 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
-
spec.add_dependency 'flt', ">= 1.
|
22
|
-
spec.add_dependency '
|
21
|
+
spec.add_dependency 'flt', ">= 1.4.7"
|
22
|
+
spec.add_dependency 'numerals', ">= 0.3.0"
|
23
23
|
|
24
24
|
spec.add_development_dependency "bundler", "~> 1.6"
|
25
25
|
spec.add_development_dependency "rake"
|
26
|
+
spec.add_development_dependency 'nio', ">= 0.2.4"
|
27
|
+
|
28
|
+
spec.required_ruby_version = '>= 1.9.3'
|
26
29
|
end
|
data/lib/float-formats/bytes.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
# Float-Formats
|
2
2
|
# Support for binary data representations
|
3
|
-
require '
|
4
|
-
require 'nio/sugar'
|
3
|
+
require 'numerals'
|
5
4
|
|
6
5
|
require 'enumerator'
|
7
6
|
require 'delegate'
|
@@ -10,8 +9,8 @@ module Flt
|
|
10
9
|
|
11
10
|
class Bits
|
12
11
|
# Define a bit string given the number of bits and
|
13
|
-
#
|
14
|
-
def initialize(num_bits,v=0)
|
12
|
+
# optionally the initial value (as an integer).
|
13
|
+
def initialize(num_bits, v=0)
|
15
14
|
@n = num_bits
|
16
15
|
@v = v
|
17
16
|
end
|
@@ -20,10 +19,9 @@ class Bits
|
|
20
19
|
def to_s(base=2)
|
21
20
|
n = Bits.power_of_two(base)
|
22
21
|
if n
|
23
|
-
fmt = Nio::Fmt.default.base(base,true).mode(:fix,:exact)
|
24
22
|
digits = (size+n-1)/n
|
25
|
-
|
26
|
-
|
23
|
+
#{ }"%0#{digits}d" % @v.to_s(base)
|
24
|
+
Numerals::Format[:fix, base: base].set_leading_zeros(digits).write(@v)
|
27
25
|
else
|
28
26
|
@v.to_s(base)
|
29
27
|
end
|
@@ -48,7 +46,7 @@ class Bits
|
|
48
46
|
end
|
49
47
|
|
50
48
|
# produce bit string from an integer
|
51
|
-
def self.from_i(v,len=nil)
|
49
|
+
def self.from_i(v, len=nil)
|
52
50
|
len ||= (Math.log(v)/Math.log(2)).ceil # v.to_s(2).size
|
53
51
|
Bits.new(len,v)
|
54
52
|
end
|
@@ -112,9 +110,8 @@ class Bytes < DelegateClass(String)
|
|
112
110
|
@bytes = bytes.pack("C*")
|
113
111
|
else
|
114
112
|
@bytes = bytes.to_str
|
115
|
-
@bytes.force_encoding("BINARY") if @bytes.respond_to?(:force_encoding)
|
116
113
|
end
|
117
|
-
@bytes.force_encoding(
|
114
|
+
@bytes.force_encoding("BINARY") if @bytes.respond_to?(:force_encoding)
|
118
115
|
super @bytes
|
119
116
|
end
|
120
117
|
|
@@ -35,9 +35,8 @@
|
|
35
35
|
# * minimum encoded exponent reserved for zero (nonzero significands are not used with it)
|
36
36
|
# * special all zeros representation: minimun encoded exponent is a regular exponent except for 0
|
37
37
|
|
38
|
-
require 'nio'
|
39
|
-
require 'nio/sugar'
|
40
38
|
require 'flt'
|
39
|
+
require 'numerals'
|
41
40
|
require 'enumerator'
|
42
41
|
require 'float-formats/bytes.rb'
|
43
42
|
|
@@ -91,59 +90,46 @@ class FormatBase
|
|
91
90
|
@sign,@significand,@exponent = v.to_a
|
92
91
|
end
|
93
92
|
end
|
93
|
+
|
94
94
|
attr_reader :sign, :significand, :exponent
|
95
|
+
|
95
96
|
def to_a
|
96
97
|
split
|
97
98
|
end
|
99
|
+
|
98
100
|
def split
|
99
101
|
return [@sign,@significand,@exponent]
|
100
102
|
end
|
101
103
|
|
102
|
-
|
103
104
|
def nan?
|
104
105
|
@exponent == :nan
|
105
106
|
end
|
107
|
+
|
106
108
|
def zero?
|
107
109
|
return @exponent==:zero || @significand==0
|
108
110
|
end
|
111
|
+
|
109
112
|
def infinite?
|
110
113
|
return @exponent==:infinity
|
111
114
|
end
|
115
|
+
|
112
116
|
def subnormal?
|
113
117
|
return @exponent==:denormal || (@significand.kind_of?(Integer) && @significand<self.class.minimum_normalized_integral_significand)
|
114
118
|
end
|
119
|
+
|
115
120
|
def normal?
|
116
121
|
@exponend.kind_of?(Integer) && @significand>=self.class.minimum_normalized_integral_significand
|
117
122
|
end
|
118
123
|
|
119
|
-
include Nio::Formattable
|
120
|
-
|
121
124
|
# from/to integral_sign_significand_exponent
|
122
125
|
|
123
|
-
def
|
124
|
-
|
125
|
-
|
126
|
-
when :zero
|
127
|
-
v = 0.0/@sign
|
128
|
-
when :infinity
|
129
|
-
v = @sign/0.0
|
130
|
-
when :nan
|
131
|
-
v = 0.0/0.0
|
132
|
-
else
|
133
|
-
case form_class.radix
|
134
|
-
when 10
|
135
|
-
v = Flt.DecNum(@sign, @significand, @exponent)
|
136
|
-
when 2
|
137
|
-
v = Flt.BinNum(@sign, @significand, @exponent)
|
138
|
-
else
|
139
|
-
v = @significand*form_class.radix_power(@exponent)*@sign
|
140
|
-
end
|
126
|
+
def to_text(fmt = Numerals::Format[])
|
127
|
+
if infinite?
|
128
|
+
fmt = fmt[symbols: [show_plus: true]]
|
141
129
|
end
|
142
|
-
|
143
|
-
end
|
144
|
-
def to_text(fmt=Nio::Fmt.default)
|
145
|
-
nio_write(fmt)
|
130
|
+
fmt.write(self)
|
146
131
|
end
|
132
|
+
|
147
133
|
def to_bytes
|
148
134
|
form_class.pack(@sign,@significand,@exponent)
|
149
135
|
end
|
@@ -154,24 +140,29 @@ class FormatBase
|
|
154
140
|
to_bits.to_hex
|
155
141
|
end
|
156
142
|
end
|
157
|
-
def to(number_class, mode
|
158
|
-
if number_class==Bytes
|
143
|
+
def to(number_class, mode = :approx)
|
144
|
+
if number_class == Bytes
|
159
145
|
to_bytes
|
160
|
-
elsif number_class==String
|
161
|
-
mode =
|
146
|
+
elsif number_class == String
|
147
|
+
mode = Numerals::Format[] if mode == :approx
|
162
148
|
to_text(mode)
|
163
|
-
elsif
|
149
|
+
elsif Numerals::Format === number_class
|
164
150
|
to_text(number_class)
|
165
|
-
elsif number_class==Array
|
151
|
+
elsif number_class == Array
|
166
152
|
split
|
167
|
-
elsif Symbol===number_class
|
153
|
+
elsif Symbol === number_class
|
168
154
|
send "to_#{number_class}"
|
169
155
|
elsif number_class.is_a?(Flt::Num) && number_class.radix == form_class.radix
|
170
156
|
self.to_num
|
157
|
+
elsif number_class.is_a?(FormatBase)
|
158
|
+
number_class.from_number(self, mode)
|
171
159
|
else # assume number_class.ancestors.include?(Numeric) (number_class < Numeric)
|
172
|
-
|
173
|
-
|
174
|
-
|
160
|
+
options = {
|
161
|
+
type: number_class,
|
162
|
+
exact_input: (mode != :approx),
|
163
|
+
output_mode: :fixed
|
164
|
+
}
|
165
|
+
Numerals::Conversions.convert(self, options)
|
175
166
|
end
|
176
167
|
end
|
177
168
|
def to_bits
|
@@ -183,13 +174,13 @@ class FormatBase
|
|
183
174
|
def to_bits_text(base)
|
184
175
|
to_bits.to_s(base)
|
185
176
|
#i = to_bits
|
186
|
-
#fmt =
|
177
|
+
#fmt = Numerals::Format[base: base]
|
187
178
|
#if [2,4,8,16].include?(base)
|
188
179
|
# n = (Math.log(base)/Math.log(2)).round.to_i
|
189
180
|
# digits = (form_class.total_bits+n-1)/n
|
190
|
-
# fmt.
|
181
|
+
# fmt.set_trailing_zeros!(digits)
|
191
182
|
#end
|
192
|
-
#i.to_i
|
183
|
+
#fmt.writ(i.to_i)
|
193
184
|
end
|
194
185
|
|
195
186
|
# Computes the negation of a floating point value (unary minus)
|
@@ -198,11 +189,10 @@ class FormatBase
|
|
198
189
|
end
|
199
190
|
|
200
191
|
# Converts a floating point value to another format
|
201
|
-
def convert_to(fpclass)
|
202
|
-
|
192
|
+
def convert_to(fpclass, options = {})
|
193
|
+
Numerals::Conversions.convert(self, options.merge(type: fpclass))
|
203
194
|
end
|
204
195
|
|
205
|
-
|
206
196
|
# Computes the next adjacent floating point value.
|
207
197
|
# Accepts either a Value or a byte String.
|
208
198
|
# Returns a Value.
|
@@ -384,7 +374,7 @@ class FormatBase
|
|
384
374
|
# originally, we incremented min_encoded_exp here unconditionally, but
|
385
375
|
# now we don't if there's no hidden bit
|
386
376
|
# (we assume the minimum exponent can be used for normalized and denormalized numbers)
|
387
|
-
# because of this, IEEE_EXTENDED & 128 formats now specify :
|
377
|
+
# because of this, IEEE_EXTENDED & 128 formats now specify min_encoded_exp: 1 in it's definitions
|
388
378
|
@min_encoded_exp += 1 if @hidden_bit
|
389
379
|
end
|
390
380
|
end
|
@@ -580,23 +570,60 @@ class FormatBase
|
|
580
570
|
Num[self.radix]
|
581
571
|
end
|
582
572
|
|
573
|
+
def self.numerals_conversion(options = {})
|
574
|
+
FltFmtConversion.new(self, options)
|
575
|
+
end
|
576
|
+
|
583
577
|
def self.context
|
584
578
|
num_class::Context.new(
|
585
|
-
:
|
586
|
-
:
|
587
|
-
:
|
588
|
-
|
579
|
+
precision: significand_digits,
|
580
|
+
emin: radix_min_exp(:scientific_significand),
|
581
|
+
emax: radix_max_exp(:scientific_significand),
|
582
|
+
rounding:@round || :half_even
|
589
583
|
)
|
590
584
|
end
|
591
585
|
|
592
586
|
def to_num
|
593
|
-
s,c,e = split
|
594
|
-
|
595
|
-
|
587
|
+
s, c, e = split
|
588
|
+
case e
|
589
|
+
when :zero
|
590
|
+
e = 0
|
591
|
+
when :infinity
|
592
|
+
e = :inf
|
593
|
+
when :nan
|
594
|
+
e = :nan
|
595
|
+
end
|
596
|
+
form_class.num_class.Num(s, c, e)
|
596
597
|
end
|
597
598
|
|
599
|
+
# def to_num
|
600
|
+
# # num_class = Flt::Num[form_class.radix]
|
601
|
+
# num_class = self.class.num_class
|
602
|
+
# case @exponent
|
603
|
+
# when :zero
|
604
|
+
# num_class.zero(@sign)
|
605
|
+
# when :infinity
|
606
|
+
# num_class.infinity(@sign)
|
607
|
+
# when :nan
|
608
|
+
# num_class.nan
|
609
|
+
# else
|
610
|
+
# num_class.new(@sign, @significand, @exponent)
|
611
|
+
# end
|
612
|
+
# end
|
613
|
+
|
598
614
|
def self.num(x)
|
599
|
-
|
615
|
+
s, c, e = x.split
|
616
|
+
if x.zero?
|
617
|
+
e = :zero
|
618
|
+
else
|
619
|
+
case e
|
620
|
+
when :inf
|
621
|
+
e = :infinity
|
622
|
+
when :nan
|
623
|
+
e = :nan
|
624
|
+
end
|
625
|
+
end
|
626
|
+
new([s, c, e])
|
600
627
|
end
|
601
628
|
|
602
629
|
# Endianness of the format (:little_endian, :big_endian or :little_big_endian)
|
@@ -792,41 +819,6 @@ class FormatBase
|
|
792
819
|
|
793
820
|
# from methods
|
794
821
|
|
795
|
-
|
796
|
-
def self.nio_read_neutral(neutral)
|
797
|
-
if neutral.special?
|
798
|
-
case neutral.special
|
799
|
-
when :nan
|
800
|
-
return nan
|
801
|
-
when :inf
|
802
|
-
return infinity(neutral.sign=='-' ? -1 : +1)
|
803
|
-
end
|
804
|
-
end
|
805
|
-
if neutral.rep_pos<neutral.digits.length
|
806
|
-
nd = neutral.base==10 ? decimal_digits_necessary : (significand_digits*Math.log(radix)/Math.log(fmt.get_base)).ceil+1
|
807
|
-
neutral = neutral.round(nd,:sig)
|
808
|
-
end
|
809
|
-
f = neutral.digits.to_i(neutral.base)
|
810
|
-
e = neutral.dec_pos-neutral.digits.length
|
811
|
-
case neutral.rounding
|
812
|
-
when :even
|
813
|
-
rounding = :half_even
|
814
|
-
when :inf
|
815
|
-
rounding = :half_up
|
816
|
-
when :zero
|
817
|
-
rounding = :half_down
|
818
|
-
when :truncate
|
819
|
-
rounding = :down
|
820
|
-
end
|
821
|
-
s = (neutral.sign=='-') ? -1 : +1
|
822
|
-
if neutral.base!=radix
|
823
|
-
reader = Flt::Support::Reader.new(:mode=>:fixed)
|
824
|
-
s,f,e = reader.read(context, rounding, s, f, e, neutral.base).split
|
825
|
-
end
|
826
|
-
return_value s,f,e
|
827
|
-
|
828
|
-
end
|
829
|
-
|
830
822
|
def self.from(*args)
|
831
823
|
new(*args)
|
832
824
|
end
|
@@ -839,17 +831,27 @@ class FormatBase
|
|
839
831
|
from_bytes Bytes.from_hex(hex)
|
840
832
|
end
|
841
833
|
|
842
|
-
def self.from_number(v, mode
|
834
|
+
def self.from_number(v, mode = :approx)
|
843
835
|
if v.is_a?(Flt::Num) && v.num_class.radix==self.radix
|
844
836
|
self.num(v)
|
845
837
|
else
|
846
|
-
|
847
|
-
|
838
|
+
options = {
|
839
|
+
type: self,
|
840
|
+
exact_input: (mode != :approx),
|
841
|
+
output_mode: @normalized ? :fixed : :short
|
842
|
+
}
|
843
|
+
Numerals::Conversions.convert(v, options)
|
848
844
|
end
|
849
845
|
end
|
850
846
|
|
851
|
-
def self.from_text(txt,
|
852
|
-
|
847
|
+
def self.from_text(txt, *args)
|
848
|
+
if @normalized
|
849
|
+
fmt = Numerals::Format[exact_input: true]
|
850
|
+
else
|
851
|
+
fmt = Numerals::Format[:short, exact_input: false]
|
852
|
+
end
|
853
|
+
fmt = fmt[*args]
|
854
|
+
fmt.read(txt, type: self)
|
853
855
|
end
|
854
856
|
|
855
857
|
def self.join(sign,significand,exponent)
|
@@ -875,8 +877,8 @@ class FormatBase
|
|
875
877
|
# Defines a floating-point number from a text representation of the
|
876
878
|
# encoded integral value in a given base.
|
877
879
|
# Returns a Value.
|
878
|
-
def self.from_bits_text(txt,base)
|
879
|
-
i =
|
880
|
+
def self.from_bits_text(txt, base)
|
881
|
+
i = Numerals::Format[].read(txt, type: Integer, base: base)
|
880
882
|
from_bits i
|
881
883
|
end
|
882
884
|
|
@@ -934,6 +936,48 @@ class FormatBase
|
|
934
936
|
end
|
935
937
|
end
|
936
938
|
|
939
|
+
class FltFmtConversion
|
940
|
+
|
941
|
+
def initialize(form_class, options={})
|
942
|
+
@form_class = form_class
|
943
|
+
@input_rounding = options[:input_rounding]
|
944
|
+
end
|
945
|
+
|
946
|
+
def type
|
947
|
+
@form_class
|
948
|
+
end
|
949
|
+
|
950
|
+
def order_of_magnitude(value, options={})
|
951
|
+
num_conversions.order_of_maginitude(value.to_num, options)
|
952
|
+
end
|
953
|
+
|
954
|
+
def number_of_digits(value, options={})
|
955
|
+
num_conversions.number_of_digits(value.to_num, options)
|
956
|
+
end
|
957
|
+
|
958
|
+
def exact?(value, options={})
|
959
|
+
options[:exact]
|
960
|
+
end
|
961
|
+
|
962
|
+
def write(number, exact_input, output_rounding)
|
963
|
+
# assert_equal @form_class, number.class
|
964
|
+
num_conversions.write(number.to_num, exact_input, output_rounding)
|
965
|
+
end
|
966
|
+
|
967
|
+
def read(numeral, exact_input, approximate_simplified)
|
968
|
+
num = num_conversions.read(numeral, exact_input, approximate_simplified)
|
969
|
+
num = num.normalize(@form_class.context) if exact_input
|
970
|
+
@form_class.num(num)
|
971
|
+
end
|
972
|
+
|
973
|
+
private
|
974
|
+
|
975
|
+
def num_conversions
|
976
|
+
Numerals::Conversions[@form_class.context, input_rounding: @input_rounding]
|
977
|
+
end
|
978
|
+
|
979
|
+
end
|
980
|
+
|
937
981
|
# :stopdoc:
|
938
982
|
protected
|
939
983
|
def self.define_fields(field_definitions)
|
@@ -1231,7 +1275,7 @@ class BCDFormat < DecimalFormatBase
|
|
1231
1275
|
end
|
1232
1276
|
s = sign_from_unit(s)
|
1233
1277
|
m,e = neg_significand_exponent(0,m,e) if s%2==1
|
1234
|
-
pack_fields_hash :
|
1278
|
+
pack_fields_hash sign: s, significand: m, exponent: e
|
1235
1279
|
end
|
1236
1280
|
# :startdoc:
|
1237
1281
|
end
|
@@ -1359,7 +1403,7 @@ class DPDFormat < DecimalFormatBase
|
|
1359
1403
|
i_combination = sig_msd|(1<<4)|(exp_msb<<1)
|
1360
1404
|
end
|
1361
1405
|
end
|
1362
|
-
h = {:
|
1406
|
+
h = {sign: i_sign, combination: i_combination, exponent_continuation: i_exponent_continuation, significand_continuation: i_significand_continuation}
|
1363
1407
|
fields = @internal_field_meaning.collect{|f| h[f]}
|
1364
1408
|
Bytes.from_bitfields(@internal_field_lengths,fields,@endianness)
|
1365
1409
|
end
|
@@ -1427,7 +1471,7 @@ class DPDFormat < DecimalFormatBase
|
|
1427
1471
|
end
|
1428
1472
|
s = sign_from_unit(s)
|
1429
1473
|
m,e = neg_significand_exponent(0,m,e) if s%2==1
|
1430
|
-
pack_fields_hash :
|
1474
|
+
pack_fields_hash sign: s, significand: m, exponent: e, type: t
|
1431
1475
|
end
|
1432
1476
|
|
1433
1477
|
# :startdoc:
|
@@ -1553,7 +1597,7 @@ class BinaryFormat < FieldsInBitsFormatBase
|
|
1553
1597
|
end
|
1554
1598
|
s = sign_from_unit(s)
|
1555
1599
|
m,e = neg_significand_exponent(0,m,e) if s%2==1
|
1556
|
-
pack_fields_hash :
|
1600
|
+
pack_fields_hash sign: s, significand: m, exponent: e
|
1557
1601
|
end
|
1558
1602
|
# :startdoc:
|
1559
1603
|
|
@@ -1646,7 +1690,7 @@ class HexadecimalFormat < FieldsInBitsFormatBase
|
|
1646
1690
|
end
|
1647
1691
|
s = sign_from_unit(s)
|
1648
1692
|
m,e = neg_significand_exponent(0,m,e) if s%2==1
|
1649
|
-
pack_fields_hash :
|
1693
|
+
pack_fields_hash sign: s, significand: m, exponent: e
|
1650
1694
|
end
|
1651
1695
|
|
1652
1696
|
|
@@ -1741,7 +1785,7 @@ end
|
|
1741
1785
|
# by adjusting the sign of the second number. This is enabled by the
|
1742
1786
|
# :extra_prec option.
|
1743
1787
|
# For example, the "double double" format used in PowerPC is this
|
1744
|
-
# Flt.define :DoubleDouble, DoubleFormat, :
|
1788
|
+
# Flt.define :DoubleDouble, DoubleFormat, half: IEEE_binary64, extra_prec: true
|
1745
1789
|
# Although this has a fixed 107 bits precision, the format as used in
|
1746
1790
|
# the PowerPC can have greater precision for specific values (by having
|
1747
1791
|
# greater separation between the exponents of both halves)
|