float-formats 0.2.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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)
|