cbor-diag 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/cbor2diag.rb +7 -0
- data/bin/cbor2json.rb +8 -0
- data/bin/cbor2pretty.rb +7 -0
- data/bin/cbor2yaml.rb +15 -0
- data/bin/diag2cbor.rb +20 -0
- data/bin/diag2pretty.rb +15 -0
- data/bin/json2cbor.rb +14 -0
- data/bin/json2pretty.rb +8 -0
- data/cbor-diag.gemspec +22 -0
- data/lib/cbor-diag-parser.rb +2127 -0
- data/lib/cbor-diagnostic.rb +74 -0
- data/lib/cbor-pretty.rb +88 -0
- data/lib/cbor-pure.rb +260 -0
- data/lib/half.rb +111 -0
- metadata +108 -0
@@ -0,0 +1,74 @@
|
|
1
|
+
# This should work with the C-ext cbor-ruby as well as with our cbor-pure
|
2
|
+
unless defined?(CBOR)
|
3
|
+
require_relative 'cbor-pure'
|
4
|
+
end
|
5
|
+
|
6
|
+
class Object
|
7
|
+
def cbor_diagnostic
|
8
|
+
inspect
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class NilClass
|
13
|
+
def cbor_diagnostic
|
14
|
+
"null"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class Float
|
19
|
+
def cbor_diagnostic # do a little bit of JSON.stringify gaming (ECMA-262, 9.8.1)
|
20
|
+
a = abs
|
21
|
+
if a < 1 && a >= 1e-6
|
22
|
+
inspect.sub(/(\d)[.](\d+)e-(\d+)/) {"0.#{"0" * ($3.to_i - 1)}#{$1}#{$2}"}
|
23
|
+
else
|
24
|
+
inspect.sub(/(e[+-])0+/) {$1}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
raise unless 0.00006103515625.cbor_diagnostic == "0.00006103515625"
|
30
|
+
raise unless 0.99.cbor_diagnostic == "0.99"
|
31
|
+
raise unless 0.099.cbor_diagnostic == "0.099"
|
32
|
+
raise unless 0.0000099.cbor_diagnostic == "0.0000099"
|
33
|
+
|
34
|
+
class String
|
35
|
+
unless String.instance_methods.include?(:b)
|
36
|
+
def b
|
37
|
+
dup.force_encoding(Encoding::BINARY)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
def hexbytes(sep = '')
|
41
|
+
bytes.map{|x| "%02x" % x}.join(sep)
|
42
|
+
end
|
43
|
+
def cbor_diagnostic
|
44
|
+
if lengths = cbor_stream?
|
45
|
+
pos = 0
|
46
|
+
"(_ #{lengths.map{|l| r = self[pos, l].cbor_diagnostic; pos += l; r}.join(", ")})"
|
47
|
+
else
|
48
|
+
if encoding == Encoding::BINARY
|
49
|
+
"h'#{hexbytes}'"
|
50
|
+
else
|
51
|
+
inspect.encode(Encoding::UTF_16BE).bytes.each_slice(2).map {
|
52
|
+
|c1, c2| c = (c1 << 8)+c2; c < 128 ? c.chr : '\u%04x' % c }.join
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class Array
|
59
|
+
def cbor_diagnostic
|
60
|
+
"[#{"_ " if cbor_stream?}#{map(&:cbor_diagnostic).join(", ")}]"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
class Hash
|
65
|
+
def cbor_diagnostic
|
66
|
+
"{#{"_ " if cbor_stream?}#{map{ |k, v| %{#{k.cbor_diagnostic}: #{v.cbor_diagnostic}}}.join(", ")}}"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
class CBOR::Tagged
|
71
|
+
def cbor_diagnostic
|
72
|
+
"#{tag}(#{data.cbor_diagnostic})"
|
73
|
+
end
|
74
|
+
end
|
data/lib/cbor-pretty.rb
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
# This should work with the C-ext cbor-ruby as well as with our cbor-pure
|
4
|
+
unless defined?(CBOR)
|
5
|
+
require_relative 'cbor-pure'
|
6
|
+
end
|
7
|
+
|
8
|
+
class String
|
9
|
+
def hexbytes(sep = '')
|
10
|
+
bytes.map{|x| "%02x" % x}.join(sep)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
class CBOR
|
16
|
+
def self.pretty(s, indent = 0, max_target = 40)
|
17
|
+
new(s).pretty_item_final(indent, max_target)
|
18
|
+
end
|
19
|
+
|
20
|
+
def take_and_print(n, prefix = '')
|
21
|
+
s = take(n)
|
22
|
+
@out << prefix
|
23
|
+
@out << s.hexbytes
|
24
|
+
s
|
25
|
+
end
|
26
|
+
|
27
|
+
def pretty_item_streaming(ib)
|
28
|
+
res = nil
|
29
|
+
@out << " # #{MT_NAMES[ib >> 5]}(*)\n"
|
30
|
+
@indent += 1
|
31
|
+
case ib >>= 5
|
32
|
+
when 2, 3, 4, 5
|
33
|
+
while (element = pretty_item) != BREAK
|
34
|
+
end
|
35
|
+
when 7; res = BREAK
|
36
|
+
else raise "unknown ib #{ib} for additional information 31"
|
37
|
+
end
|
38
|
+
@indent -= 1
|
39
|
+
res
|
40
|
+
end
|
41
|
+
|
42
|
+
MT_NAMES = ["unsigned", "negative", "bytes", "text", "array", "map", "tag", "primitive"]
|
43
|
+
|
44
|
+
def pretty_item
|
45
|
+
ib = take_and_print(1, ' ' * @indent).ord
|
46
|
+
ai = ib & 0x1F
|
47
|
+
val = case ai
|
48
|
+
when 0...24; ai
|
49
|
+
when 24; take_and_print(1, ' ').ord
|
50
|
+
when 25; take_and_print(2, ' ').unpack("n").first
|
51
|
+
when 26; (s = take_and_print(4, ' ')).unpack("N").first
|
52
|
+
when 27; (s = take_and_print(8, ' ')).unpack("Q>").first
|
53
|
+
when 31; return pretty_item_streaming(ib)
|
54
|
+
else raise "unknown additional information #{ai} in ib #{ib}"
|
55
|
+
end
|
56
|
+
@out << " # #{MT_NAMES[ib >> 5]}(#{val})\n"
|
57
|
+
@indent += 1
|
58
|
+
case ib >>= 5
|
59
|
+
when 6
|
60
|
+
pretty_item
|
61
|
+
when 2, 3
|
62
|
+
@out << ' ' * (@indent)
|
63
|
+
s = take_and_print(val)
|
64
|
+
@out << " # #{s.inspect}"
|
65
|
+
@out << "\n"
|
66
|
+
when 4; val.times { pretty_item }
|
67
|
+
when 5; val.times { pretty_item; pretty_item}
|
68
|
+
end
|
69
|
+
@indent -= 1
|
70
|
+
nil
|
71
|
+
end
|
72
|
+
|
73
|
+
def pretty_item_final(indent = 0, max_target = 40)
|
74
|
+
@out = ''
|
75
|
+
@indent = indent
|
76
|
+
pretty_item
|
77
|
+
raise if @pos != @buffer.size
|
78
|
+
target = [@out.each_line.map {|ln| ln =~ /#/ || 0}.max, max_target].min
|
79
|
+
@out.each_line.map {|ln|
|
80
|
+
col = ln =~ /#/
|
81
|
+
if col && col < target
|
82
|
+
ln[col, 0] = ' ' * (target - col)
|
83
|
+
end
|
84
|
+
ln
|
85
|
+
}.join
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
data/lib/cbor-pure.rb
ADDED
@@ -0,0 +1,260 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require_relative "half.rb"
|
4
|
+
|
5
|
+
|
6
|
+
class CBOR
|
7
|
+
module Streaming
|
8
|
+
def cbor_stream?
|
9
|
+
@cbor_streaming
|
10
|
+
end
|
11
|
+
def cbor_stream!(b = true)
|
12
|
+
@cbor_streaming = b
|
13
|
+
self
|
14
|
+
end
|
15
|
+
end
|
16
|
+
Array.send(:include, Streaming)
|
17
|
+
Hash.send(:include, Streaming)
|
18
|
+
String.send(:include, Streaming)
|
19
|
+
|
20
|
+
class Break
|
21
|
+
end
|
22
|
+
BREAK = Break.new.freeze
|
23
|
+
|
24
|
+
Tagged = Struct.new(:tag, :data) do
|
25
|
+
def to_s
|
26
|
+
"#{tag}(#{data})"
|
27
|
+
end
|
28
|
+
def inspect
|
29
|
+
"#{tag}(#{data.inspect})"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
TAG_BIGNUM_BASE = 2
|
34
|
+
|
35
|
+
Simple = Struct.new(:value) do
|
36
|
+
def to_s
|
37
|
+
if value == 23
|
38
|
+
"undefined"
|
39
|
+
else
|
40
|
+
"simple(#{value})"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
alias_method :inspect, :to_s
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.encode(d)
|
47
|
+
new.add(d).buffer
|
48
|
+
end
|
49
|
+
def self.decode(s)
|
50
|
+
new(s).decode_item_final
|
51
|
+
end
|
52
|
+
|
53
|
+
attr_reader :buffer
|
54
|
+
def initialize(s = String.new)
|
55
|
+
@buffer = s
|
56
|
+
@pos = 0
|
57
|
+
end
|
58
|
+
|
59
|
+
def head(ib, n)
|
60
|
+
@buffer <<
|
61
|
+
case n
|
62
|
+
when 0...24
|
63
|
+
[ib + n].pack("C")
|
64
|
+
when 0...256
|
65
|
+
[ib + 24, n].pack("CC")
|
66
|
+
when 0...65536
|
67
|
+
[ib + 25, n].pack("Cn")
|
68
|
+
when 0...4294967296
|
69
|
+
[ib + 26, n].pack("CN")
|
70
|
+
when 0...18446744073709551616
|
71
|
+
[ib + 27, n].pack("CQ>")
|
72
|
+
else
|
73
|
+
yield # throw back to caller
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
HALF_NAN_BYTES = ("\xf9".force_encoding(Encoding::BINARY) + Half::NAN_BYTES).freeze
|
78
|
+
|
79
|
+
def addfloat(fv)
|
80
|
+
if fv.nan?
|
81
|
+
@buffer << HALF_NAN_BYTES
|
82
|
+
else
|
83
|
+
ss = [fv].pack("g") # single-precision
|
84
|
+
if ss.unpack("g").first == fv
|
85
|
+
if hs = Half.encode_from_single(fv, ss)
|
86
|
+
@buffer << 0xf9 << hs
|
87
|
+
else
|
88
|
+
@buffer << 0xfa << ss
|
89
|
+
end
|
90
|
+
else
|
91
|
+
@buffer << [0xfb, fv].pack("CG") # double-precision
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def bignum_to_bytes(d)
|
97
|
+
s = String.new
|
98
|
+
while (d != 0)
|
99
|
+
s << (d & 0xFF)
|
100
|
+
d >>= 8
|
101
|
+
end
|
102
|
+
s.reverse!
|
103
|
+
end
|
104
|
+
|
105
|
+
def add(d)
|
106
|
+
case d
|
107
|
+
when Integer
|
108
|
+
ib = if d < 0
|
109
|
+
d = -1-d
|
110
|
+
0x20
|
111
|
+
else
|
112
|
+
0x00
|
113
|
+
end
|
114
|
+
head(ib, d) { # block is called if things do not fit
|
115
|
+
s = bignum_to_bytes(d)
|
116
|
+
head(0xc0, TAG_BIGNUM_BASE + (ib >> 5))
|
117
|
+
head(0x40, s.bytesize)
|
118
|
+
s
|
119
|
+
}
|
120
|
+
when Numeric; addfloat(d)
|
121
|
+
when Symbol; add(d.to_s) # hack: this should really be tagged
|
122
|
+
when Simple; head(0xe0, d.value)
|
123
|
+
when false; head(0xe0, 20)
|
124
|
+
when true; head(0xe0, 21)
|
125
|
+
when nil; head(0xe0, 22)
|
126
|
+
when Tagged # we don't handle :simple here
|
127
|
+
head(0xc0, d.tag)
|
128
|
+
add(d.data)
|
129
|
+
when String
|
130
|
+
lengths = d.cbor_stream?
|
131
|
+
e = d
|
132
|
+
ib = if d.encoding == Encoding::BINARY
|
133
|
+
0x40
|
134
|
+
else
|
135
|
+
d = d.encode(Encoding::UTF_8).force_encoding(Encoding::BINARY)
|
136
|
+
0x60
|
137
|
+
end
|
138
|
+
if lengths
|
139
|
+
@buffer << (ib + 31)
|
140
|
+
pos = 0
|
141
|
+
lengths.each do |r|
|
142
|
+
add(e[pos, r])
|
143
|
+
pos += r
|
144
|
+
end
|
145
|
+
@buffer << 0xff
|
146
|
+
else
|
147
|
+
head(ib, d.bytesize)
|
148
|
+
@buffer << d
|
149
|
+
end
|
150
|
+
when Array
|
151
|
+
if d.cbor_stream?
|
152
|
+
@buffer << 0x9f
|
153
|
+
d.each {|di| add(di)}
|
154
|
+
@buffer << 0xff
|
155
|
+
else
|
156
|
+
head(0x80, d.size)
|
157
|
+
d.each {|di| add(di)}
|
158
|
+
end
|
159
|
+
when Hash
|
160
|
+
if d.cbor_stream?
|
161
|
+
@buffer << 0xbf
|
162
|
+
d.each {|k, v| add(k); add(v)}
|
163
|
+
@buffer << 0xff
|
164
|
+
else
|
165
|
+
head(0xa0, d.size)
|
166
|
+
d.each {|k, v| add(k); add(v)}
|
167
|
+
end
|
168
|
+
else
|
169
|
+
raise("Don't know how to encode #{d.inspect}")
|
170
|
+
end
|
171
|
+
self
|
172
|
+
end
|
173
|
+
|
174
|
+
def take(n)
|
175
|
+
opos = @pos
|
176
|
+
@pos += n
|
177
|
+
raise "Out of bytes to decode: #{opos} + #{n} > #{@buffer.bytesize}" if @pos > @buffer.bytesize
|
178
|
+
@buffer[opos, n]
|
179
|
+
end
|
180
|
+
|
181
|
+
MT_TO_ENCODING = {2 => Encoding::BINARY, 3 => Encoding::UTF_8}
|
182
|
+
|
183
|
+
def decode_item_streaming(ib, breakable)
|
184
|
+
case ib >>= 5
|
185
|
+
when 2, 3
|
186
|
+
want_encoding = MT_TO_ENCODING[ib]
|
187
|
+
subs = []
|
188
|
+
while (element = decode_item(true)) != BREAK
|
189
|
+
raise "non-string (#{element.inspect}) in streaming string" unless String === element
|
190
|
+
raise "bytes/text mismatch (#{element.encoding} != #{want_encoding}) in streaming string" unless element.encoding == want_encoding
|
191
|
+
subs << element
|
192
|
+
end
|
193
|
+
result = subs.join.cbor_stream!(subs.map(&:length)).force_encoding(want_encoding)
|
194
|
+
when 4
|
195
|
+
result = Array.new;
|
196
|
+
while (element = decode_item(true)) != BREAK
|
197
|
+
result << element
|
198
|
+
end
|
199
|
+
result
|
200
|
+
when 5
|
201
|
+
result = Hash.new
|
202
|
+
while (key = decode_item(true)) != BREAK
|
203
|
+
result[key] = decode_item
|
204
|
+
end
|
205
|
+
result
|
206
|
+
when 7
|
207
|
+
raise "break stop code outside indefinite length item" unless breakable
|
208
|
+
BREAK
|
209
|
+
else raise "unknown ib #{ib} for additional information 31"
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
def decode_item(breakable = false)
|
214
|
+
ib = take(1).ord
|
215
|
+
ai = ib & 0x1F
|
216
|
+
val = case ai
|
217
|
+
when 0...24; ai
|
218
|
+
when 24; take(1).ord
|
219
|
+
when 25; take(2).unpack("n").first
|
220
|
+
when 26; (s = take(4)).unpack("N").first
|
221
|
+
when 27; (s = take(8)).unpack("Q>").first
|
222
|
+
when 31; return decode_item_streaming(ib, breakable)
|
223
|
+
else raise "unknown additional information #{ai} in ib #{ib}"
|
224
|
+
end
|
225
|
+
case ib >>= 5
|
226
|
+
when 0; val
|
227
|
+
when 1; -1-val
|
228
|
+
when 7
|
229
|
+
case ai
|
230
|
+
when 20; false
|
231
|
+
when 21; true
|
232
|
+
when 22; nil
|
233
|
+
# when 27; Simple.new(27) # Ruby does not have Undefined
|
234
|
+
when 25; Half.decode(val)
|
235
|
+
when 26; s.unpack("g").first # cannot go directly from val
|
236
|
+
when 27; s.unpack("G").first # in Ruby
|
237
|
+
else
|
238
|
+
Simple.new(val)
|
239
|
+
end
|
240
|
+
when 6
|
241
|
+
di = decode_item
|
242
|
+
if String === di && (val & ~1) == TAG_BIGNUM_BASE
|
243
|
+
(TAG_BIGNUM_BASE - val) ^ di.bytes.inject(0) {|sum, b| sum <<= 8; sum += b }
|
244
|
+
else
|
245
|
+
Tagged.new(val, di)
|
246
|
+
end
|
247
|
+
when 2; take(val).force_encoding(Encoding::BINARY)
|
248
|
+
when 3; take(val).force_encoding(Encoding::UTF_8)
|
249
|
+
when 4; Array.new(val) { decode_item }
|
250
|
+
when 5; Hash[Array.new(val) {[decode_item, decode_item]}]
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
def decode_item_final
|
255
|
+
val = decode_item
|
256
|
+
raise "extra bytes follow after a deserialized object" if @pos != @buffer.size
|
257
|
+
val
|
258
|
+
end
|
259
|
+
|
260
|
+
end
|
data/lib/half.rb
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
#
|
3
|
+
# 16-bit floating point values (IEEE 754 Half Precision) are not
|
4
|
+
# supported by #pack/#unpack in Ruby yet.
|
5
|
+
# This is a quick hack implementing en- and decoding them.
|
6
|
+
# (Since this is just a hack, the brief tests are in this file.)
|
7
|
+
#
|
8
|
+
# The encoder assumes that we already have a Single-Precision byte
|
9
|
+
# string (e.g., from pack("g")), and this is taken apart and
|
10
|
+
# reassembled.
|
11
|
+
# The decoder is free-standing (trivial).
|
12
|
+
#
|
13
|
+
# IEEE 754 can be found at:
|
14
|
+
# http://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=4610935
|
15
|
+
|
16
|
+
module Half
|
17
|
+
NAN_BYTES = "\x7e\x00"
|
18
|
+
|
19
|
+
def self.decode_from_bytes(hs)
|
20
|
+
b16, = hs.unpack("n")
|
21
|
+
self.decode(b16)
|
22
|
+
end
|
23
|
+
def self.decode(b16)
|
24
|
+
exp = b16 >> 10 & 0x1f
|
25
|
+
mant = b16 & 0x3ff
|
26
|
+
val =
|
27
|
+
if exp == 0
|
28
|
+
Math.ldexp(mant, -24)
|
29
|
+
elsif exp == 31
|
30
|
+
mant == 0 ? Float::INFINITY : Float::NAN
|
31
|
+
else
|
32
|
+
Math.ldexp(0x400 + mant, exp-25)
|
33
|
+
end
|
34
|
+
if b16[15] != 0
|
35
|
+
-val
|
36
|
+
else
|
37
|
+
val
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.encode_from_single_bytes(ss) # single-precision string
|
42
|
+
b32, = ss.unpack("N")
|
43
|
+
s16 = b32 >> 16 & 0x8000
|
44
|
+
mant = b32 & 0x7fffff
|
45
|
+
exp = b32 >> 23 & 0xff
|
46
|
+
# puts "#{fv} #{s16} #{mant.to_s(16)} #{exp}"
|
47
|
+
if exp == 0
|
48
|
+
s16 if mant == 0 # 0.0, -0.0
|
49
|
+
elsif exp >= 103 && exp < 113 # denorm, exp16 = 0
|
50
|
+
s16 + ((mant + 0x800000) >> (126 - exp))
|
51
|
+
elsif exp >= 113 && exp <= 142 # normalized
|
52
|
+
s16 + ((exp - 112) << 10) + (mant >> 13)
|
53
|
+
elsif exp == 255 # Inf (handle NaN elsewhere!)
|
54
|
+
s16 + 0x7c00 if mant == 0 # +Inf/-Inf
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.encode_from_single(fv, ss)
|
59
|
+
if e = self.encode_from_single_bytes(ss)
|
60
|
+
# p e.to_s(16)
|
61
|
+
hs = [e].pack("n")
|
62
|
+
hs if self.decode_from_bytes(hs) == fv
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.encode(fv)
|
67
|
+
self.encode_from_single(fv, [fv].pack("g"))
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
(-24..15).each do |i|
|
73
|
+
f = Math.ldexp(1, i)
|
74
|
+
s = Half.encode(f)
|
75
|
+
fail i unless s
|
76
|
+
end
|
77
|
+
(-24..6).each do |i|
|
78
|
+
f = Math.ldexp(1023, i)
|
79
|
+
s = Half.encode(f)
|
80
|
+
fail i unless s
|
81
|
+
end
|
82
|
+
|
83
|
+
# p Half.decode("\x7b\xff") 65504.0
|
84
|
+
|
85
|
+
[ # go through Wikipedia samples
|
86
|
+
0b0_01111_0000000000, 1.0,
|
87
|
+
0b0_01111_0000000001, 1.0 + Math.ldexp(1, -10), # = 1 + 2−10 = 1.0009765625 (next biggest float after 1)
|
88
|
+
0b1_10000_0000000000, -2.0,
|
89
|
+
|
90
|
+
0b0_11110_1111111111, 65504.0, # (max half precision)
|
91
|
+
|
92
|
+
0b0_00001_0000000000, Math.ldexp(1, -14), # ≈ 6.10352 × 10−5 (minimum positive normal)
|
93
|
+
0b0_00000_1111111111, Math.ldexp(1, -14) - Math.ldexp(1, -24), # ≈ 6.09756 × 10−5 (maximum subnormal)
|
94
|
+
0b0_00000_0000000001, Math.ldexp(1, -24), # ≈ 5.96046 × 10−8 (minimum positive subnormal)
|
95
|
+
|
96
|
+
0b0_00000_0000000000, 0.0,
|
97
|
+
0b1_00000_0000000000, -0.0,
|
98
|
+
|
99
|
+
0b0_11111_0000000000, 1.0/0.0,
|
100
|
+
0b1_11111_0000000000, -1.0/0.0,
|
101
|
+
|
102
|
+
0b0_01101_0101010101, 0.333251953125 #... ≈ 1/3
|
103
|
+
].each_slice(2) do |hv, expected|
|
104
|
+
fv = Half.decode(hv)
|
105
|
+
raise [hv, fv, expected].inspect unless fv == expected
|
106
|
+
end
|
107
|
+
|
108
|
+
# NaN cannot be compared, so this one needs to be special-cased:
|
109
|
+
raise "NaN not detected" unless Half.decode(0b0_11111_1000000000).nan?
|
110
|
+
raise "-NaN not detected" unless Half.decode(0b1_11111_1000000000).nan?
|
111
|
+
raise "NaN not detected" unless Half.decode_from_bytes(Half::NAN_BYTES).nan?
|