float-formats 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,1550 @@
1
+ # Float-Formats
2
+ # Floating-point classes -- tools to handle floating point formats
3
+
4
+ # Nomenclature:
5
+ # * radix: the numerical base of the floating point notation
6
+ # * significand: the digits part of the number; also called coefficient or, arguably, mantissa.
7
+ # * exponent: the radix exponent, also called characteristic;
8
+ # * encoded exponent: the value stored in the f.p. format to denote the exponent
9
+ #
10
+ # Significand modes determine the interpretation of the significand value and also
11
+ # of the exponent values. For excess-notation encoded exponents this means that
12
+ # the value of the bias depends upon the interpretation of the significand.
13
+ # * integral significand: the significand is interpreted as an integer, i.e. the radix
14
+ # point is at the right (after the last digit);
15
+ # Some examples of this pont of view:
16
+ # - HP-50G LONGFLOAT library
17
+ # - HP-16C Floating Point-Integer conversion
18
+ # * fractional significand: the radix point is at the left, before the first significand digit
19
+ # (before the hidden bit if there is one); the value v of a normalized significand is 1/r<=v<1
20
+ # Examples:
21
+ # - The C standard library frexp/ldexp uses this interpretation, and the <float.h> constants.
22
+ # (and so this is also used by Ruby's Math.frexp and the constants defined in Float)
23
+ # - Don Knuth's The Art of Computer Programming, Vol.2
24
+ # - BigDecimal#split
25
+ # * normalized significand: the radix point is after the first digit (after the hidden bit if present)
26
+ # the value v of a normalized significand is 1<=v<r
27
+ # Examples:
28
+ # - IEEE 754 definitions
29
+ # - RPL's MANT/XPON
30
+ # - HP calculators SCI mode
31
+ #
32
+ # zero representation in hidden bit formats:
33
+ # * with gradual underflow: the minimium encoded exponent (0) is used for zero and denormal numbers;
34
+ # i.e. the minimum encoded exponent implies the hidden bit is 0
35
+ # * minimum encoded exponent reserved for zero (nonzero significands are not used with it)
36
+ # * special all zeros representation: minimun encoded exponent is a regular exponent except for 0
37
+
38
+ require 'nio'
39
+ require 'nio/sugar'
40
+ require 'enumerator'
41
+ require 'float-formats/bytes.rb'
42
+
43
+ module FltPnt
44
+
45
+ # General Floating Point Format Base class
46
+ class FormatBase
47
+ include FltPnt
48
+
49
+ # Common parameters for all floating-point formats:
50
+ # [<tt>:bias_mode</tt>] This defines how the significand is interpreted:
51
+ # * <tt>:fractional_significand</tt> The radix point is before the most significant
52
+ # digit of the significand (including a hidden bit if there's one).
53
+ # * <tt>:normalized_significand</tt> The radix point is after the most significant
54
+ # digit of the significand (including a hidden bit if there's one).
55
+ # * <tt>:integral_significand</tt> The significand is assumed to be an integer, i.e.
56
+ # the radix point is after the least significant digit.
57
+ # [<tt>:bias</tt>] Defines the exponent encoding method to be excess notation
58
+ # and defines the bias.
59
+ # [<tt>:endianness</tt>] Defines the byte endianness. One of:
60
+ # * <tt>:little_endian</tt> Least significant bytes come first. (Intel etc.)
61
+ # * <tt>:big_endian</tt> (Network order): most significant bytes come first. (Motorola, SPARC,...)
62
+ # * <tt>:little_big_endian</tt> (Middle endian) Each pair of bytes (16-bit word) has the bytes in
63
+ # little endian order, but the words are stored in big endian order
64
+ # (we assume the number of bytes is even). (PDP-11).
65
+ def initialize(params={})
66
+
67
+ @fields_handler = params[:fields_handler]
68
+
69
+ @exponent_radix = params[:exponent_radix] || radix
70
+
71
+ @zero_encoded_exp = params[:zero_encoded_exp] || 0
72
+ @min_encoded_exp = params[:min_encoded_exp] || 0
73
+ @denormal_encoded_exp = params[:denormal_encoded_exp]
74
+ @gradual_underflow = params[:gradual_underflow] || (@denormal_encoded_exp ? true : false)
75
+ if @gradual_underflow
76
+ @denormal_encoded_exp = 0 if !@denormal_encoded_exp
77
+ if @denormal_encoded_exp>=@min_encoded_exp
78
+ @min_encoded_exp = @denormal_encoded_exp
79
+ # originally, we incremented min_encoded_exp here unconditionally, but
80
+ # now we don't if there's no hidden bit
81
+ # (we assume the minimum exponent can be used for normalized and denormalized numbers)
82
+ # because of this, IEEE_EXTENDED & 128 formats now specify :min_encoded_exp=>1 in it's definitions
83
+ @min_encoded_exp += 1 if @hidden_bit
84
+ end
85
+ end
86
+ # Note that if there's a hidden bit and no gradual underflow, the minimum encoded exponent will only
87
+ # be used for zero unless a parameter :min_encoded_exp (=0) is passed. In this case all numbers with
88
+ # minimun exponent and nonzero encoded significand will have a 1-valued hidden bit. Otherwise
89
+ # the only valid encoded significand with minimun encoded exponent is 0.
90
+ # In case of gradual underflow, the minimum exponent implies a hidden bit value of 0
91
+ @min_encoded_exp += 1 if @min_encoded_exp==@zero_encoded_exp && (@hidden_bit && params[:min_encoded_exp].nil?)
92
+
93
+ @infinite_encoded_exp = params[:infinite_encoded_exp]
94
+ @nan_encoded_exp = params[:nan_encoded_exp]
95
+
96
+ @infinity = params[:infinity] || (@infinite_encoded_exp ? true : false)
97
+ @max_encoded_exp = params[:max_encoded_exp] || @exponent_radix**@fields[:exponent]-1 # maximum regular exponent, encoded
98
+ if @infinity
99
+ @infinite_encoded_exp = @nan_encoded_exp || @max_encoded_exp if !@infinite_encoded_exp
100
+ @max_encoded_exp = @infinite_encoded_exp - 1 if @infinite_encoded_exp<=@max_encoded_exp
101
+ end
102
+ @nan = params[:nan] || (@nan_encoded_exp ? true : false)
103
+ if @nan
104
+ @nan_encoded_exp = @infinite_encoded_exp || @max_encoded_exp if !@nan_encoded_exp
105
+ @max_encoded_exp = @nan_encoded_exp - 1 if @nan_encoded_exp<=@max_encoded_exp
106
+ end
107
+
108
+ @exponent_mode = params[:exponent_mode]
109
+ if @exponent_mode.nil?
110
+ if params[:bias]
111
+ @exponent_mode = :excess
112
+ else
113
+ @exponent_mode = :radix_complement
114
+ end
115
+ end
116
+ @exponent_digits = @fields[:exponent]
117
+
118
+ if @exponent_mode==:excess
119
+ @bias = params[:bias] || (@exponent_radix**(@fields[:exponent]-1)-1)
120
+ @bias_mode = params[:bias_mode] || :normalized_significand
121
+ @min_exp = params[:min_exp]
122
+ @max_exp = params[:max_exp]
123
+ case @bias_mode
124
+ when :integral_significand
125
+ @integral_bias = @bias
126
+ @fractional_bias = @integral_bias-@significand_digits
127
+ @normalized_bias = @fractional_bias+1
128
+ when :fractional_significand
129
+ @fractional_bias = @bias
130
+ @integral_bias = @fractional_bias+@significand_digits
131
+ @normalized_bias = @fractional_bias+1
132
+ @min_exp -= @significand_digits if @min_exp
133
+ @max_exp -= @significand_digits if @max_exp
134
+ when :normalized_significand
135
+ @normalized_bias = @bias
136
+ @fractional_bias = @normalized_bias-1
137
+ @integral_bias = @fractional_bias+@significand_digits
138
+ @min_exp -= @significand_digits-1 if @min_exp
139
+ @max_exp -= @significand_digits-1 if @max_exp
140
+ end
141
+ else
142
+ #@bias_mode = :normalized_significand
143
+ @min_exp = params[:min_exp] || (-(@exponent_radix**@exponent_digits)/2 + 1)
144
+ @max_exp = params[:max_exp] || ((@exponent_radix**@exponent_digits)/2 - 1)
145
+ end
146
+ @endianness = params[:endianness] || :little_endian
147
+
148
+ @min_exp = @min_encoded_exp - @integral_bias if @min_exp.nil?
149
+ @max_exp = @max_encoded_exp - @integral_bias if @max_exp.nil?
150
+
151
+ if @exponent_mode==:excess
152
+ @integral_max_exp = @max_exp
153
+ @integral_min_exp = @min_exp
154
+ @fractional_max_exp = @max_exp+@significand_digits
155
+ @fractional_min_exp = @min_exp+@significand_digits
156
+ @normalized_max_exp = @max_exp+@significand_digits-1
157
+ @normalized_min_exp = @min_exp+@significand_digits-1
158
+ else
159
+ @integral_max_exp = @max_exp - (@significand_digits-1)
160
+ @integral_min_exp = @min_exp - (@significand_digits-1)
161
+ @fractional_max_exp = @max_exp+1
162
+ @fractional_min_exp = @min_exp+1
163
+ @normalized_max_exp = @max_exp
164
+ @normalized_min_exp = @min_exp
165
+ end
166
+
167
+ @round = params[:round] # || :even
168
+
169
+ @neg_mode = params[:neg_mode] || :sign_magnitude
170
+
171
+ yield self if block_given?
172
+
173
+ end
174
+
175
+ # compute a power of the radix (base)
176
+ def radix_power(n)
177
+ radix**n
178
+ end
179
+
180
+ # base-10 logarithm of the radix
181
+ def radix_log10
182
+ Math.log(radix)/Math.log(10)
183
+ end
184
+ # radix-base logarithm
185
+ def radix_log(x)
186
+ Math.log(x)/Math.log(radix)
187
+ end
188
+ # number of decimal digits that can be stored in a floating point value and restored unaltered
189
+ def decimal_digits_stored
190
+ ((significand_digits-1)*radix_log10).floor
191
+ end
192
+ # number of decimal digits necessary to unambiguosly define any floating point value
193
+ def decimal_digits_necessary
194
+ (significand_digits*radix_log10).ceil+1
195
+ end
196
+
197
+ # Maximum integer such that 10 raised to that power
198
+ # is in the range of the normalised floating point numbers
199
+ def decimal_max_exp
200
+ (radix_max_exp(:fractional_significand)*radix_log10+(1-radix_power(-significand_digits))*radix_log10).floor
201
+ #(Math.log((1-radix**(significand_digits))*radix**radix_max_exp(:fractional_significand))/Math.log(10)).floor
202
+ end
203
+ # Minimum negative integer such that 10 raised to that power
204
+ # is in the range of the normalised floating point numbers
205
+ def decimal_min_exp
206
+ (radix_min_exp(:fractional_significand)*radix_log10).ceil
207
+ end
208
+ # Stored (integral) value for the minus sign
209
+ def minus_sign_value
210
+ (-1) % radix
211
+ end
212
+ # switch between the encodings of minus and plus
213
+ def switch_sign_value(v)
214
+ (v==0) ? minus_sign_value : 0
215
+ end
216
+
217
+ # Rounding mode use for this floating-point format; can be one of:
218
+ # [<tt>:even</tt>] round to nearest with ties toward an even digits
219
+ # [<tt>:inf</tt>] round to nearest with ties toward infinity (away from zero)
220
+ # [<tt>:zero</tt>] round to nearest with ties toward zero
221
+ # [<tt>nil</tt>] rounding mode not specified
222
+ def rounding_mode
223
+ @round
224
+ end
225
+
226
+ # Number of digits in the significand (precision)
227
+ def significand_digits
228
+ @significand_digits
229
+ end
230
+
231
+ # This is a helper to handle floating point values with the auxiliar class Value:
232
+ # it is used to return an encoded floating point value as a Value.
233
+ def return_bytes(v)
234
+ if v.kind_of?(Value)
235
+ v
236
+ else
237
+ Value.new self, v
238
+ end
239
+ end
240
+ # This is a helper to handle floating point values with the auxiliar class Value:
241
+ # it is used to get a bytes string from either a Value or a String
242
+ def input_bytes(v)
243
+ if v.kind_of?(Value)
244
+ raise "Invalid f.p. format" if v.fp_format!=self
245
+ v = v.bytes
246
+ elsif !v.kind_of?(String)
247
+ raise "Invalid f.p. object"
248
+ v = nil
249
+ end
250
+ v
251
+ end
252
+
253
+ # Greatest finite normalized floating point number in the representation.
254
+ # It can be made negative by passing the sign (a so this would be the smallest
255
+ # finite number).
256
+ def max_value(sign=0)
257
+ s = sign
258
+ m = radix_power(significand_digits)-1
259
+ e = radix_max_exp(:integral_significand)
260
+ from_integral_sign_significand_exponent(s,m,e)
261
+ end
262
+ # Smallest normalized floating point number greater than zero in the representation.
263
+ # It can be made negative by passing the sign.
264
+ def min_normalized_value(sign=0)
265
+ s = sign
266
+ m = 1
267
+ e = radix_min_exp(:normalized_significand)
268
+ if @hidden_bit && (@min_encoded_exp==@zero_encoded_exp)
269
+ m = 1 + radix_power(significand_digits - 1)
270
+ e = radix_min_exp(:integral_significand)
271
+ else
272
+ m = 1
273
+ e = radix_min_exp(:normalized_significand)
274
+ end
275
+ from_integral_sign_significand_exponent(s,m,e)
276
+ end
277
+ # Smallest floating point number greater than zero in the representation, including
278
+ # denormalized values if the representation supports it.
279
+ # It can be made negative by passing the sign.
280
+ def min_value(sign=0)
281
+ if @denormal_encoded_exp
282
+ s = sign
283
+ m = 1
284
+ e = :denormal
285
+ from_integral_sign_significand_exponent(s,m,e)
286
+ else
287
+ min_normalized_value(sign)
288
+ end
289
+ end
290
+ # This is the difference between 1.0 and the smallest floating-point
291
+ # value greater than 1.0, radix_power(1-significand_precision)
292
+ def epsilon(sign=0)
293
+ s = sign
294
+ #m = 1
295
+ #e = 1-significand_digits
296
+ m = radix_power(significand_digits-1)
297
+ e = 2*(1-significand_digits)
298
+ from_integral_sign_significand_exponent(s,m,e)
299
+ end
300
+ # The strict epsilon is the smallest value that produces something different from 1.0
301
+ # wehen added to 1.0. It may be smaller than the general epsilon, because
302
+ # of the particular rounding rules used with the floating point format.
303
+ # This is only meaningful when well-defined rules are used for rounding the result
304
+ # of floating-point addition.
305
+ # Note that (in pseudo-code):
306
+ # ((1.0+strict_epsilon)-1.0)==epsilon
307
+ def strict_epsilon(sign=0, round=nil)
308
+ round ||= @round
309
+ s = sign
310
+ m = radix_power(significand_digits-1)
311
+ e = 2*(1-significand_digits)
312
+ # assume radix is even
313
+ case @round
314
+ when :even, :zero, :any_rounding
315
+ e -= 1
316
+ m /= 2
317
+ m *= radix
318
+ m += 1
319
+ when :inf
320
+ # note that here :inf means "round to nearest with ties tower infinity"
321
+ e -= 1
322
+ m /= 2
323
+ m *= radix
324
+ when :ceiling, :up
325
+ # this is the IEEE "toward infinity" rounding
326
+ return min_value
327
+ end
328
+ from_integral_sign_significand_exponent(s,m,e)
329
+ end
330
+ # This is the maximum relative error corresponding to 1/2 ulp:
331
+ # (radix/2)*radix_power(-significand_precision) == epsilon/2
332
+ # This is called "machine epsilon" in [Goldberg]
333
+ def half_epsilon(sign=0)
334
+ s = sign
335
+ m = radix/2
336
+ e = -significand_digits
337
+ # normalize:
338
+ m *= radix_power(significand_digits-1)
339
+ e -= significand_digits-1
340
+ from_integral_sign_significand_exponent(s,m,e)
341
+ end
342
+
343
+ # Floating point representation of zero.
344
+ def zero(sign=0)
345
+ from_integral_sign_significand_exponent sign, 0, :zero
346
+ end
347
+ # Floating point representation of infinity.
348
+ def infinity(sign=0)
349
+ if @infinite_encoded_exp
350
+ from_integral_sign_significand_exponent sign, 0, :infinity
351
+ else
352
+ nil
353
+ end
354
+ end
355
+ # Floating point representation of Not-a-Number.
356
+ def nan
357
+ if @nan_encoded_exp
358
+ from_integral_sign_significand_exponent 0, 0, :nan
359
+ else
360
+ nil
361
+ end
362
+ end
363
+
364
+ # Maximum exponent
365
+ def radix_max_exp(significand_mode=:normalized_significand)
366
+ case significand_mode
367
+ when :integral_significand
368
+ @integral_max_exp
369
+ when :fractional_significand
370
+ @fractional_max_exp
371
+ when :normalized_significand
372
+ @normalized_max_exp
373
+ end
374
+ end
375
+ # Mminimum exponent
376
+ def radix_min_exp(significand_mode=:normalized_significand)
377
+ case significand_mode
378
+ when :integral_significand
379
+ @integral_min_exp
380
+ when :fractional_significand
381
+ @fractional_min_exp
382
+ when :normalized_significand
383
+ @normalized_min_exp
384
+ end
385
+ end
386
+
387
+ # Endianness of the format (:little_endian, :big_endian or :little_big_endian)
388
+ def endianness
389
+ @endianness
390
+ end
391
+
392
+ # Does the format support gradual underflow? (denormalized numbers)
393
+ def gradual_underflow?
394
+ @gradual_underflow
395
+ end
396
+
397
+ # Exponent bias for excess notation exponent encoding
398
+ # The argument defines the interpretation of the significand and so
399
+ # determines the actual bias value.
400
+ def bias(significand_mode=:normalized_significand)
401
+ case significand_mode
402
+ when :integral_significand
403
+ @integral_bias
404
+ when :fractional_significand
405
+ @fractional_bias
406
+ when :normalized_significand
407
+ @normalized_bias
408
+ end
409
+ end
410
+
411
+ # Produce an encoded floating point value using a number defined by a
412
+ # formatted text string (using Nio formats). Returns a Value.
413
+ def from_fmt(txt,fmt=Nio::Fmt.default)
414
+ neutral = fmt.nio_read_formatted(txt)
415
+ if neutral.rep_pos<neutral.digits.length
416
+ nd = fmt.get_base==10 ? decimal_digits_necessary : (significand_digits*Math.log(radix)/Math.log(fmt.get_base)).ceil+1
417
+ fmt = fmt.mode(:sig,nd)
418
+ neutral = fmt.nio_read_formatted(fmt.nio_write_formatted(fmt.nio_read_formatted(txt)))
419
+ end
420
+ f = neutral.digits.to_i(neutral.base)
421
+ e = neutral.dec_pos-neutral.digits.length
422
+ rounding = neutral.rounding
423
+ if neutral.base==radix
424
+ x = from_integral_sign_significand_exponent(0,f,e)
425
+ else
426
+ x = algM(f,e,rounding,neutral.base)
427
+ end
428
+ if neutral.sign=='-'
429
+ x = neg(x)
430
+ end
431
+ x
432
+ end
433
+ # Formats an encoded floating point value as a text string (using Nio formats)
434
+ # Accepts either a Value or a byte String.
435
+ def to_fmt(v,fmt=Nio::Fmt.default)
436
+ # this always treats the floating-point values as exact quantities
437
+ s,m,e = to_integral_sign_significand_exponent(v)
438
+ case e
439
+ when :zero
440
+ v = 0.0/(s==0 ? +1 : -1)
441
+ when :infinity
442
+ v = (s==0 ? +1.0 : -1.0)/0.0
443
+ when :nan
444
+ v = 0.0/0.0
445
+ else
446
+ v = m*radix_power(e)*((-1)**s)
447
+ end
448
+ v.nio_write(fmt)
449
+ end
450
+
451
+ # Produce an encoded floating point value using a numeric value.
452
+ # Returns a Value.
453
+ def from_number(v, mode=:approx)
454
+ fmt = mode==:approx ? Nio::Fmt::CONV_FMT : Nio::Fmt::CONV_FMT_STRICT
455
+ from_fmt(v.nio_write(fmt),fmt)
456
+ end
457
+ # Computes the value in a specified numeric class (Float, Integer, Rational, BigDecimal)
458
+ # of an encoded floating point number.
459
+ # Accepts either a Value or a byte String.
460
+ def to_number(v, number_class, mode=:approx)
461
+ fmt = mode==:approx ? Nio::Fmt::CONV_FMT : Nio::Fmt::CONV_FMT_STRICT
462
+ v = to_fmt(v,fmt)
463
+ number_class.nio_read(v,fmt)
464
+ end
465
+
466
+ # Converts an ancoded floating point number to an hexadecimal representation;
467
+ # bytes are separated if the second argument is true.
468
+ # Accepts either a Value or a byte String.
469
+ def to_hex(v,sep_bytes=false)
470
+ bytes_to_hex(input_bytes(v),sep_bytes)
471
+ end
472
+ # Produce an encoded floating point value using an hexadecimal representation.
473
+ # Returns a Value.
474
+ def from_hex(hex)
475
+ return_bytes hex_to_bytes(hex)
476
+ end
477
+
478
+ # Converts an ancoded floating point number to hash containing
479
+ # the internal fields that define the number.
480
+ # Accepts either a Value or a byte String.
481
+ def to_fields_hash(v)
482
+ a = to_fields(v)
483
+ h = {}
484
+ if @splitted_fields.nil?
485
+ (0...a.size).each do |i|
486
+ h[@field_meaning[i]] = a[i]
487
+ end
488
+ else
489
+
490
+ @fields.each_key do |f|
491
+ splits = @splitted_fields[f]
492
+ if splits
493
+ v = 0
494
+ k = 1
495
+ splits.each do |i|
496
+ v += k*a[i]
497
+ k *= fields_radix**(@field_lengths[i])
498
+ end
499
+ h[f] = v
500
+ else
501
+ h[f] = a[@field_meaning.index(f)]
502
+ end
503
+ end
504
+ end
505
+ h
506
+ end
507
+ # Produce an encoded floating point value using hash of the internal field values.
508
+ # Returns a Value.
509
+ def from_fields_hash(h)
510
+ if @splitted_fields.nil?
511
+ from_fields @field_meaning.collect{|f| h[f]}
512
+ else
513
+ flds = [nil]*@field_meaning.size
514
+ @fields.each_key do |f|
515
+ splits = @splitted_fields[f]
516
+ if splits
517
+ v = h[f]
518
+ splits.each do |i|
519
+ k = fields_radix**(@field_lengths[i])
520
+ flds[i] = v % k
521
+ v /= k
522
+ end
523
+ else
524
+ flds[@field_meaning.index(f)] = h[f]
525
+ end
526
+ end
527
+ from_fields flds
528
+ end
529
+ end
530
+
531
+ # Computes the next adjacent floating point value.
532
+ # Accepts either a Value or a byte String.
533
+ # Returns a Value.
534
+ def next_float(v)
535
+ s,f,e = to_integral_sign_significand_exponent(v)
536
+ return neg(prev_float(neg(v))) if s!=0 && e!=:zero
537
+ s = switch_sign_value(s) if e==:zero && s!=0
538
+
539
+ if e!=:nan && e!=:infinity
540
+ if f==0
541
+ if @gradual_underflow
542
+ e = radix_min_exp(:integral_significand)
543
+ f = 1
544
+ else
545
+ e = radix_min_exp(:integral_significand)
546
+ f = radix_power(@significand_digits-1)
547
+ end
548
+ else
549
+ f += 1
550
+ end
551
+ if f>=radix_power(@significand_digits)
552
+ f /= radix
553
+ e += 1
554
+ if e>radix_max_exp(:integral_significand)
555
+ e = :infinity
556
+ f = 0
557
+ end
558
+ end
559
+ from_integral_sign_significand_exponent(s,f,e)
560
+ end
561
+ end
562
+
563
+ # Computes the previous adjacent floating point value.
564
+ # Accepts either a Value or a byte String.
565
+ # Returns a Value.
566
+ def prev_float(v)
567
+ s,f,e = to_integral_sign_significand_exponent(v)
568
+ return neg(next_float(neg(v))) if s!=0
569
+ return neg(next_float(v)) if e==:zero
570
+ if e!=:nan && e!=:infinity
571
+ f -= 1
572
+ if f<radix_power(@significand_digits-1)
573
+ if e>radix_min_exp(:integral_significand)
574
+ e -= 1
575
+ f *= radix
576
+ else
577
+ if @gradual_underflow
578
+ e = :denormal
579
+ else
580
+ e = :zero
581
+ end
582
+ end
583
+ end
584
+ from_integral_sign_significand_exponent(s,f,e)
585
+ end
586
+ end
587
+
588
+ # Produce an encoded floating point value from the integral value
589
+ # of the sign, significand and exponent.
590
+ # Returns a Value.
591
+ def from_fractional_sign_significand_exponent(sign,fraction,exponent)
592
+ msb = radix_power(@significand_digits-1)
593
+ while fraction<msb
594
+ fraction *= radix
595
+ exponent -= 1
596
+ end
597
+ from_integral_sign_significand_exponent(sign,fraction.to_i,exponent)
598
+ end
599
+ # Computes an encoded floating-point value from the integral value
600
+ # Accepts either a Value or a byte String.
601
+ def to_fractional_sign_significand_exponent(v,significand_mode=:normalized_significand)
602
+ s,m,e = to_integral_sign_significand_exponent(v)
603
+ case significand_mode
604
+ when :integral_significand
605
+ when :fractional_significand
606
+ m = Rational(m,radix_power(@significand_digits))
607
+ when :normalized_significand
608
+ m = Rational(m,radix_power(@significand_digits-1))
609
+ end
610
+ end
611
+
612
+ # Returns the encoded value of a floating-point number as an integer
613
+ # Accepts either a Value or a byte String.
614
+ def to_bits_integer(v)
615
+ bytes_to_int(input_bytes(v), @endianness)
616
+ end
617
+ # Defines a floating-point number from the encoded integral value.
618
+ # Returns a Value.
619
+ def from_bits_integer(i)
620
+ v = int_to_bytes(i)
621
+ if v.size<total_bytes
622
+ fill = (0.chr*(total_bytes-v.size))
623
+ if @endianness==:little_endian
624
+ bytes = fill
625
+ else
626
+ bytes = fill + bytes
627
+ end
628
+ elsif v.size>total_bytes
629
+ raise "Invalid floating point value"
630
+ end
631
+ return_bytes v
632
+ end
633
+ # Returns the encoded integral value of a floating point number
634
+ # as a text representation in a given base.
635
+ # Accepts either a Value or a byte String.
636
+ def to_bits_text(v,base)
637
+ i = to_bits_integer(v)
638
+ fmt = Nio::Fmt.default.base(base,true).mode(:fix,:exact)
639
+ if [2,4,8,16].include?(base)
640
+ n = (Math.log(base)/Math.log(2)).round.to_i
641
+ digits = (total_bits+n-1)/n
642
+ fmt.pad0s!(digits)
643
+ end
644
+ i.nio_write(fmt)
645
+ end
646
+ # Defines a floating-point number from a text representation of the
647
+ # encoded integral value in a given base.
648
+ # Returns a Value.
649
+ def from_bits_text(txt,base)
650
+ i = Integer.nio_read(txt,Nio::Fmt.base(base))
651
+ from_bits_integer i
652
+ end
653
+
654
+ # Computes the negation of a floating point value
655
+ # Accepts either a Value or a byte String.
656
+ # Returns a Value.
657
+ def neg(v)
658
+ s,f,e = to_integral_sign_significand_exponent(v)
659
+ s = switch_sign_value(s)
660
+ from_integral_sign_significand_exponent(s,f,e)
661
+ end
662
+
663
+ # Converts a floating point value to another format
664
+ def convert_to(v,fpclass)
665
+ fpclass.from_fmt(to_fmt(v))
666
+ end
667
+
668
+ # :stopdoc:
669
+ protected
670
+ def define_fields(field_definitions)
671
+
672
+ @field_lengths = []
673
+ @field_meaning = []
674
+ @fields = {}
675
+ @splitted_fields = nil
676
+ field_definitions.enum_slice(2).each do |m,l|
677
+ @field_lengths << l
678
+ @field_meaning << m
679
+ if @fields[m].nil?
680
+ @fields[m] = l
681
+ else
682
+ @splitted_fields ||= {}
683
+ @splitted_fields[m] = [@field_meaning.index(m)]
684
+ @fields[m] += l
685
+ @splitted_fields[m] <<= @field_meaning.size-1
686
+ end
687
+ end
688
+ raise "Invalid format" if @splitted_fields && !@splitted_fields_supported
689
+ end
690
+ def handle_fields(fields)
691
+ if @fields_handler
692
+ @fields_handler.call(fields)
693
+ end
694
+ end
695
+
696
+ # s is the sign of f,e
697
+ def neg_significand_exponent(s,f,e)
698
+ case @neg_mode
699
+ when :diminished_radix_complement
700
+ if exponent_radix==radix
701
+ f = (radix_power(significand_digits)-1) - f
702
+ e = (radix_power(exponent_digits)-1) - e
703
+ else
704
+ # unsupported case; we could use the exponent radix for
705
+ # the complement (so for hexadecimal significand/binary exponent we would use binary which is OK)
706
+ raise "Unsupported format"
707
+ end
708
+ when :radix_complement_significand
709
+ f = ((radix_power(significand_digits)-1) - f + 1) % radix_power(significand_digits)
710
+ when :radix_complement
711
+ if exponent_radix==radix
712
+ if @field_meaning.index(:exponent) < @field_meaning.index(:significand)
713
+ ls,ms = e,f
714
+ ls_digits = exponent_digits
715
+ else
716
+ ls,ms = f,e
717
+ ls_digits = significand_digits
718
+ end
719
+ comb = ls + ms*radix_power(ls_digits)
720
+ comb = ((radix_power(significand_digits)-1) - comb + 1) % radix_power(significand_digits)
721
+ ls = comb % radix_power(ls_digits)
722
+ ms = comb / radix_power(ls_digits)
723
+ if @field_meaning.index(:exponent) < @field_meaning.index(:significand)
724
+ e,f = ls,ms
725
+ else
726
+ e,f = ms,ls
727
+ end
728
+ else
729
+ # unsupported case; we could use the exponent radix for
730
+ # the complement (so for hexadecimal significand/binary exponent we would use binary which is OK)
731
+ raise "Unsupported format"
732
+ end
733
+ end
734
+ [f,e]
735
+ end
736
+ def exponent_digits
737
+ @fields[:exponent]
738
+ end
739
+ def exponent_radix
740
+ @exponent_radix
741
+ end
742
+ # radix of the field sizes, used for splitted fields
743
+ def fields_radix
744
+ radix
745
+ end
746
+
747
+ def decode_exponent(e,mode)
748
+ case @exponent_mode
749
+ when :excess
750
+ case mode
751
+ when :integral_significand
752
+ e -= @integral_bias
753
+ when :fractional_significand
754
+ e -= @fractional_bias
755
+ when :normalized_significand
756
+ e -= @normalized_bias
757
+ end
758
+ when :radix_complement
759
+ n = @fields[:exponent]
760
+ v = radix_power(n)
761
+ e = e-v if e >= v/2
762
+ case mode
763
+ when :integral_significand
764
+ e -= (significand_digits-1)
765
+ when :fractional_significand
766
+ e += 1
767
+ when :normalized_significand
768
+ end
769
+ end
770
+ e
771
+ end
772
+ def encode_exponent(e,mode)
773
+ case @exponent_mode
774
+ when :excess
775
+ case mode
776
+ when :integral_significand
777
+ e += @integral_bias
778
+ when :fractional_significand
779
+ e += @fractional_bias
780
+ when :normalized_significand
781
+ e += @normalized_bias
782
+ end
783
+ when :radix_complement
784
+ case mode
785
+ when :integral_significand
786
+ e += (significand_digits-1)
787
+ when :fractional_significand
788
+ e -= 1
789
+ when :normalized_significand
790
+ end
791
+ n = @fields[:exponent]
792
+ v = radix_power(n)
793
+ e = e+v if e < 0
794
+ end
795
+ e
796
+ end
797
+
798
+ # these are functions from Nio::Clinger, generalized for arbitrary floating point formats
799
+ def ratio_float(u,v,k,round_mode)
800
+ q = u.div v
801
+ r = u-q*v
802
+ v_r = v-r
803
+ z = from_integral_sign_significand_exponent(0,q,k)
804
+ if r<v_r
805
+ z
806
+ elsif r>v_r
807
+ z = next_float(z)
808
+ elsif (round_mode==:even && q.even?) || (round_mode==:zero)
809
+ z
810
+ else
811
+ z = next_float(z)
812
+ end
813
+ end
814
+
815
+ def algM(f,e,round_mode,eb=10)
816
+
817
+ if e<0
818
+ u,v,k = f,eb**(-e),0
819
+ else
820
+ u,v,k = f*(eb**e),1,0
821
+ end
822
+
823
+ n = significand_digits
824
+ min_e = radix_min_exp(:integral_significand)
825
+ max_e = radix_max_exp(:integral_significand)
826
+
827
+ rp_n = radix_power(n)
828
+ rp_n_1 = radix_power(n-1)
829
+ r = radix
830
+
831
+ loop do
832
+ x = u.div(v) # bottleneck
833
+ # overflow if k>=max_e
834
+ if (x>=rp_n_1 && x<rp_n) || k==min_e || k==max_e
835
+ return ratio_float(u,v,k,round_mode)
836
+ elsif x<rp_n_1
837
+ u *= r
838
+ k -= 1
839
+ elsif x>=rp_n
840
+ v *= r
841
+ k += 1
842
+ end
843
+ end
844
+
845
+ end
846
+ # :startdoc:
847
+ end
848
+
849
+ # Base class for decimal floating point formats
850
+ class DecimalFormatBase < FormatBase
851
+ # :stopdoc:
852
+ def initialize(params)
853
+ @hidden_bit = false
854
+ super params
855
+ end
856
+ def radix
857
+ 10
858
+ end
859
+ def radix_power(n)
860
+ 10**n
861
+ end
862
+
863
+ def radix_log10
864
+ 1
865
+ end
866
+ def radix_log(x)
867
+ Math.log(x)/Math.log(10)
868
+ end
869
+
870
+ def decimal_digits_stored
871
+ significand_digits
872
+ end
873
+ def decimal_digits_necessary
874
+ significand_digits
875
+ end
876
+ def decimal_max_exp
877
+ radix_max_exp(:normalized_significand)
878
+ end
879
+ def decimal_min_exp
880
+ radix_min_exp(:normalized_significand)
881
+ end
882
+
883
+ def to_fmt(v,fmt=Nio::Fmt.default)
884
+ # this always treats the floating-point values as exact quantities
885
+ s,m,e = to_integral_sign_significand_exponent(v)
886
+ case e
887
+ when :zero
888
+ v = 0.0/(s==0 ? +1 : -1)
889
+ when :infinity
890
+ v = (s==0 ? +1.0 : -1.0)/0.0
891
+ when :nan
892
+ v = 0.0/0.0
893
+ else
894
+ sign = s==0 ? '' : '-'
895
+ v = BigDecimal("#{sign}#{m}E#{e}")
896
+ end
897
+ v.nio_write(fmt)
898
+ end
899
+ # :startdoc:
900
+ end
901
+
902
+ # BCD (Binary-Coded-Decimal) floating point formats
903
+ class BCDFormat < DecimalFormatBase
904
+ # The fields lengths are defined in decimal digits
905
+ def initialize(params)
906
+
907
+ @splitted_fields_supported = false
908
+ define_fields params[:fields]
909
+
910
+ @significand_digits = @fields[:significand]
911
+ super params
912
+
913
+ end
914
+ # :stopdoc:
915
+ def total_nibbles
916
+ @field_lengths.inject{|x,y| x+y}
917
+ end
918
+ def total_bytes
919
+ (total_nibbles+1)/2
920
+ end
921
+ def total_bits
922
+ 4*total_nibbles
923
+ end
924
+
925
+ def to_fields(v)
926
+ # bytes have always nibbles in big-endian order
927
+ v = reverse_byte_nibbles(convert_endianness(input_bytes(v),@endianness,:little_endian))
928
+ nibbles = bytes_to_hex(v)
929
+ # now we have a little endian nibble string
930
+ nibble_fields = []
931
+ i = 0
932
+ for l in @field_lengths
933
+ nibble_fields << nibbles[i,l]
934
+ i += l
935
+ end
936
+ # now we conver the nibble strings to numbers
937
+ i = -1
938
+ nibble_fields.collect{|ns| i+=1;bcd_field?(i) ? ns.reverse.to_i : ns.reverse.to_i(16)}
939
+ end
940
+ def from_fields(*fields)
941
+ fields = fields[0] if fields.size==1 and fields[0].kind_of?(Array)
942
+ handle_fields fields
943
+ i = 0
944
+ nibbles = ""
945
+ for l in @field_lengths
946
+ fmt = bcd_field?(i) ? 'd' : 'X'
947
+ nibbles << ("%0#{l}#{fmt}" % fields[i]).reverse
948
+ i += 1
949
+ end
950
+ v = hex_to_bytes(nibbles)
951
+ v = convert_endianness(reverse_byte_nibbles(v),:little_endian,@endianness)
952
+ return_bytes v
953
+ end
954
+ # this has beed added to allow some fields to contain binary integers rather than bcd
955
+ def bcd_field?(i)
956
+ true
957
+ end
958
+
959
+ # assume @exponent_mode==:radix_complement
960
+
961
+ def to_integral_sign_significand_exponent(v)
962
+ f = to_fields_hash(v)
963
+ m = f[:significand]
964
+ e = f[:exponent]
965
+ s = f[:sign]
966
+ m,e = neg_significand_exponent(s,m,e) if s%2==1
967
+ if m==0
968
+ # +-zero
969
+ e = :zero
970
+ elsif @infinite_encoded_exp && e==@infinite_encoded_exp && m==0
971
+ # +-inifinity
972
+ e = :infinity
973
+ elsif @nan_encoded_exp && e==@nan_encoded_exp && m!=0
974
+ # NaN
975
+ e = :nan
976
+ else
977
+ # normalized number
978
+ e = decode_exponent(e, :integral_significand)
979
+ end
980
+ [s,m,e]
981
+ end
982
+
983
+ def from_integral_sign_significand_exponent(s,m,e)
984
+ msb = radix_power(@significand_digits-1)
985
+ if e==:zero
986
+ e = @zero_encoded_exp
987
+ m = 0
988
+ elsif e==:infinity
989
+ e = @infinite_encoded_exp || radix_power(@fields[:exponent])-1
990
+ m = 0
991
+ elsif e==:nan
992
+ e = @infinite_encoded_exp || radix_power(@fields[:exponent])-1
993
+ s = minus_sign_value # ?
994
+ m = radix_power(@significand_digits-2) if m==0
995
+ elsif e==:denormal
996
+ e = @denormal_encoded_exp
997
+ else
998
+ # to do: try to adjust m to keep e in range if out of valid range
999
+ # to do: reduce m and adjust e if m too big
1000
+
1001
+ min_exp = radix_min_exp(:integral_significand)
1002
+ if m>0
1003
+ while m<msb && e>min_exp
1004
+ e -= 1
1005
+ m *= radix
1006
+ end
1007
+ end
1008
+ if m<msb && @denormal_encoded_exp
1009
+ e = @denormal_encoded_exp
1010
+ elsif m==0 # => && @denormal_encoded_exp.nil?
1011
+ e = 0
1012
+ else
1013
+ e = encode_exponent(e, :integral_significand)
1014
+ end
1015
+ end
1016
+ m,e = neg_significand_exponent(0,m,e) if s%2==1
1017
+ from_fields_hash :sign=>s, :significand=>m, :exponent=>e
1018
+ end
1019
+ # :startdoc:
1020
+ end
1021
+
1022
+ # DPD (Densely-Packed-Decimal) formats
1023
+ class DPDFormat < DecimalFormatBase
1024
+ # The field that need to be defined (with lenghts given in decimal digits) are
1025
+ # * :sign
1026
+ # * :combination
1027
+ # * :exponent_continuation
1028
+ # * :significand_continuation
1029
+ def initialize(params)
1030
+
1031
+ @splitted_fields_supported = false
1032
+ define_fields params[:fields]
1033
+ @internal_field_lengths = @field_lengths
1034
+ @internal_field_meaning = @field_meaning
1035
+ @internal_fields = @fields
1036
+ raise "Unsupported DPD format" unless @internal_fields[:combination]==5 && @internal_fields[:sign]==1 && [0,4,7].include?(@internal_fields[:significand_continuation]%10)
1037
+
1038
+
1039
+ @exponent_bits = 2 + @internal_fields[:exponent_continuation]
1040
+
1041
+ extra_bits = (@internal_fields[:significand_continuation] % 10)
1042
+ extra_digits = (extra_bits==0) ? 0 : ((extra_bits==4) ? 1 : 2)
1043
+ @significand_digits = 1 + 3*(@internal_fields[:significand_continuation]/10) + extra_digits
1044
+
1045
+ define_fields [:type,1,:sign,1,:exponent,@exponent_bits,:significand,@significand_digits]
1046
+
1047
+ if params[:bias].nil?
1048
+
1049
+ params[:bias_mode] = :normalized_significand
1050
+
1051
+ @exp_limit = 3*(2**@internal_fields[:exponent_continuation])-1
1052
+
1053
+ params[:max_exp] = @exp_limit/2
1054
+ params[:min_exp] = -params[:max_exp]
1055
+ params[:max_exp] += 1 if (@exp_limit%2)==1
1056
+ params[:bias]= -params[:min_exp]
1057
+
1058
+ end
1059
+
1060
+ super params
1061
+
1062
+ end
1063
+
1064
+ # :stopdoc:
1065
+ def total_bits
1066
+ @internal_field_lengths.inject{|x,y| x+y}
1067
+ end
1068
+ def total_bytes
1069
+ (total_bits+7)/8
1070
+ end
1071
+
1072
+
1073
+ def to_fields(v)
1074
+ # convert internal binary fields to bcd decoded fields
1075
+ a = get_bitfields(input_bytes(v),@internal_field_lengths,@endianness)
1076
+ h = {}
1077
+ (0...a.size).each do |i|
1078
+ h[@internal_field_meaning[i]] = a[i]
1079
+ end
1080
+
1081
+ i_sign = h[:sign]
1082
+ i_combination = h[:combination]
1083
+ i_exponent_continuation = h[:exponent_continuation]
1084
+ i_significand_continuation = h[:significand_continuation]
1085
+
1086
+
1087
+ type = nil
1088
+ sign = i_sign==0 ? 0 : 9
1089
+
1090
+ a,b,c,d,e = ("%05B"%i_combination).split('').collect{|bit| bit.to_i}
1091
+
1092
+ exp_msb = 0
1093
+ sig_msd = 0
1094
+
1095
+ if a==0 || b==0
1096
+ exp_msb = (a<<1)|b
1097
+ sig_msd = (c<<2)|(d<<1)|e
1098
+ elsif c==0 || d==0
1099
+ exp_msb = (c<<1)|d
1100
+ sig_msd = (1<<3)|e
1101
+ elsif e==0
1102
+ type = :infinity
1103
+ else
1104
+ type = :nan
1105
+ end
1106
+
1107
+
1108
+ hex_sig = sig_msd.to_s
1109
+ hex_sig << dpd_to_hexbcd(i_significand_continuation,@internal_fields[:significand_continuation])
1110
+ significand = hex_sig.to_i
1111
+
1112
+ exponent = i_exponent_continuation | (exp_msb << (@exponent_bits-2))
1113
+ [type,sign,exponent,significand]
1114
+ end
1115
+ def from_fields(*fields)
1116
+ fields = fields[0] if fields.size==1 and fields[0].kind_of?(Array)
1117
+ handle_fields fields
1118
+ type,sign,exponent,significand = fields
1119
+
1120
+ sig_hex = "%0#{@significand_digits-1}d"%significand
1121
+ sig_cont_bits = 4*(@significand_digits-1)
1122
+ sig_msd = sig_hex[0,1].to_i
1123
+ i_significand_continuation,bits = hexbcd_to_dpd(sig_hex[1..-1])
1124
+
1125
+
1126
+
1127
+ exp_msb = exponent>>(@exponent_bits-2)
1128
+ i_exponent_continuation = exponent&((1<<(@exponent_bits-2))-1)
1129
+
1130
+ i_sign = ((sign==0) ? 0 : 1)
1131
+
1132
+
1133
+ case type
1134
+ when :infinity
1135
+ i_combination = 0x1E
1136
+ when :nan
1137
+ i_combination = 0x1F
1138
+ else
1139
+ if sig_msd&0x8==0
1140
+ i_combination = sig_msd|(exp_msb<<3)
1141
+ else
1142
+ i_combination = sig_msd|(1<<4)|(exp_msb<<1)
1143
+ end
1144
+ end
1145
+ h = {:sign=>i_sign, :combination=>i_combination, :exponent_continuation=>i_exponent_continuation, :significand_continuation=>i_significand_continuation}
1146
+ fields = @internal_field_meaning.collect{|f| h[f]}
1147
+ return_bytes set_bitfields(@internal_field_lengths,fields,@endianness)
1148
+ end
1149
+
1150
+
1151
+ def to_integral_sign_significand_exponent(v)
1152
+ f = to_fields_hash(v)
1153
+ m = f[:significand]
1154
+ e = f[:exponent]
1155
+ s = f[:sign]
1156
+ t = f[:type]
1157
+ m,e = neg_significand_exponent(s,m,e) if s%2==1
1158
+ if m==0
1159
+ # +-zero
1160
+ e = :zero
1161
+ elsif t==:infinity
1162
+ # +-inifinity
1163
+ e = :infinity
1164
+ elsif t==:nan
1165
+ # NaN
1166
+ e = :nan
1167
+ else
1168
+ # normalized number
1169
+ e = decode_exponent(e, :integral_significand)
1170
+ end
1171
+ [s,m,e]
1172
+ end
1173
+
1174
+ def from_integral_sign_significand_exponent(s,m,e)
1175
+ msb = radix_power(@significand_digits-1)
1176
+ #puts "DEC FROM #{s} #{m} #{e}"
1177
+ t = nil
1178
+ if e==:zero
1179
+ e = @zero_encoded_exp
1180
+ m = 0
1181
+ elsif e==:infinity
1182
+ e = @infinite_encoded_exp || radix_power(@fields[:exponent])-1
1183
+ m = 0
1184
+ t = :infinity
1185
+ elsif e==:nan
1186
+ t = :nan
1187
+ e = 0
1188
+ s = 0
1189
+ m = 0
1190
+ elsif e==:denormal
1191
+ e = @denormal_encoded_exp || @zero_encoded_exp
1192
+ else
1193
+ # to do: try to adjust m to keep e in range if out of valid range
1194
+ # to do: reduce m and adjust e if m too big
1195
+
1196
+ min_exp = radix_min_exp(:integral_significand)
1197
+ if m>0 && false
1198
+ while m<msb && e>min_exp
1199
+ e -= 1
1200
+ m *= radix
1201
+ end
1202
+ end
1203
+ if m<msb && @denormal_encoded_exp && false
1204
+ e = @denormal_encoded_exp
1205
+ elsif m==0 # => && @denormal_encoded_exp.nil?
1206
+ e = 0
1207
+ else
1208
+ e = encode_exponent(e, :integral_significand)
1209
+ end
1210
+ end
1211
+ m,e = neg_significand_exponent(0,m,e) if s%2==1
1212
+ from_fields_hash :sign=>s, :significand=>m, :exponent=>e, :type=>t
1213
+ end
1214
+
1215
+ # :startdoc:
1216
+
1217
+
1218
+ end
1219
+
1220
+ # This is a base class for formats that specify the field lengths in bits
1221
+ class FieldsInBitsFormatBase < FormatBase
1222
+ # :stopdoc:
1223
+ def fields_radix
1224
+ 2
1225
+ end
1226
+ def to_fields(v)
1227
+ get_bitfields(input_bytes(v),@field_lengths,@endianness)
1228
+ end
1229
+ def from_fields(*fields)
1230
+ fields = fields[0] if fields.size==1 and fields[0].kind_of?(Array)
1231
+ handle_fields fields
1232
+ return_bytes set_bitfields(@field_lengths,fields,@endianness)
1233
+ end
1234
+ # :startdoc:
1235
+ end
1236
+
1237
+ # Binary floating point formats
1238
+ class BinaryFormat < FieldsInBitsFormatBase
1239
+ # a hidden bit can be define with :hidden_bit
1240
+ def initialize(params)
1241
+ @hidden_bit = params[:hidden_bit]
1242
+ @hidden_bit = true if @hidden_bit.nil?
1243
+
1244
+ @splitted_fields_supported = true
1245
+ define_fields params[:fields]
1246
+
1247
+ @significand_digits = @fields[:significand] + (@hidden_bit ? 1 : 0)
1248
+ super params
1249
+
1250
+ end
1251
+
1252
+ # :stopdoc:
1253
+
1254
+ def radix
1255
+ 2
1256
+ end
1257
+ def radix_power(n)
1258
+ if n>=0
1259
+ 1 << n
1260
+ else
1261
+ 2**n
1262
+ end
1263
+ end
1264
+
1265
+ def total_bits
1266
+ @field_lengths.inject{|x,y| x+y}
1267
+ end
1268
+ def total_bytes
1269
+ (total_bits+7)/8
1270
+ end
1271
+
1272
+ def to_integral_sign_significand_exponent(v)
1273
+ f = to_fields_hash(v)
1274
+ m = f[:significand]
1275
+ e = f[:exponent]
1276
+ s = f[:sign]
1277
+ m,e = neg_significand_exponent(s,m,e) if s%2==1
1278
+ if (m==0 && !@hidden_bit) ||
1279
+ (m==0 && (e==@zero_encoded_exp || e==@denormal_encoded_exp)) ||
1280
+ (e==@zero_encoded_exp && @min_encoded_exp>@zero_encoded_exp && !@denormal_encoded_exp)
1281
+ # +-zero
1282
+ e = :zero
1283
+ elsif (e==@denormal_encoded_exp)
1284
+ e = decode_exponent(e, :integral_significand)
1285
+ e += 1 if @denormal_encoded_exp==@zero_encoded_exp
1286
+ elsif @infinite_encoded_exp && e==@infinite_encoded_exp && m==0
1287
+ # +-inifinity
1288
+ e = :infinity
1289
+ elsif @nan_encoded_exp && e==@nan_encoded_exp && m!=0
1290
+ # NaN
1291
+ e = :nan
1292
+ else
1293
+ # normalized number
1294
+ e = decode_exponent(e, :integral_significand)
1295
+ m |= radix_power(@significand_digits-1) if @hidden_bit
1296
+ end
1297
+ [s,m,e]
1298
+ end
1299
+
1300
+ def from_integral_sign_significand_exponent(s,m,e)
1301
+ msb = radix_power(@significand_digits-1)
1302
+
1303
+ if e==:zero
1304
+ e = @zero_encoded_exp || @denormal_encoded_exp
1305
+ m = 0
1306
+ elsif e==:infinity
1307
+ e = @infinite_encoded_exp || radix_power(@fields[:exponent])-1
1308
+ m = 0
1309
+ elsif e==:nan
1310
+ e = @infinite_encoded_exp || radix_power(@fields[:exponent])-1
1311
+ s = minus_sign_value # ?
1312
+ m = radix_power(@significand_digits-2) if m==0
1313
+ elsif e==:denormal
1314
+ e = @denormal_encoded_exp
1315
+ else
1316
+ # to do: try to adjust m to keep e in range if out of valid range
1317
+ # to do: reduce m and adjust e if m too big
1318
+
1319
+ min_exp = radix_min_exp(:integral_significand)
1320
+ if m>0
1321
+ while m<msb && e>min_exp
1322
+ e -= 1
1323
+ m <<= 1 # m *= radix
1324
+ end
1325
+ end
1326
+ if m<msb && @denormal_encoded_exp
1327
+ e = @denormal_encoded_exp
1328
+ elsif m==0 # => && @denormal_encoded_exp.nil?
1329
+ e = 0
1330
+ else
1331
+ m &= (radix_power(@significand_digits-1)-1) if @hidden_bit
1332
+ e = encode_exponent(e, :integral_significand)
1333
+ end
1334
+ end
1335
+ m,e = neg_significand_exponent(0,m,e) if s%2==1
1336
+ from_fields_hash :sign=>s, :significand=>m, :exponent=>e
1337
+ end
1338
+ # :startdoc:
1339
+
1340
+ end
1341
+
1342
+ # Hexadecimal floating point format
1343
+ class HexadecimalFormat < FieldsInBitsFormatBase
1344
+ def initialize(params)
1345
+ @hidden_bit = false
1346
+ @splitted_fields_supported = true
1347
+ define_fields params[:fields]
1348
+ @significand_digits = @fields[:significand]/4
1349
+ params[:exponent_radix] = 2
1350
+ super params
1351
+ end
1352
+
1353
+ # :stopdoc:
1354
+
1355
+ def radix
1356
+ 16
1357
+ end
1358
+ def fields_radix
1359
+ exponent_radix
1360
+ end
1361
+
1362
+ def total_bits
1363
+ @field_lengths.inject{|x,y| x+y}
1364
+ end
1365
+ def total_bytes
1366
+ (total_bits+7)/8
1367
+ end
1368
+
1369
+
1370
+ def to_integral_sign_significand_exponent(v)
1371
+ f = to_fields_hash(v)
1372
+ m = f[:significand]
1373
+ e = f[:exponent]
1374
+ s = f[:sign]
1375
+ m,e = neg_significand_exponent(s,m,e) if s%2==1
1376
+ if m==0
1377
+ # +-zero
1378
+ e = :zero
1379
+ elsif @infinite_encoded_exp && e==@infinite_encoded_exp && m==0
1380
+ # +-inifinity
1381
+ e = :infinity
1382
+ elsif @nan_encoded_exp && e==@nan_encoded_exp && m!=0
1383
+ # NaN
1384
+ e = :nan
1385
+ else
1386
+ # normalized number
1387
+ e = decode_exponent(e, :integral_significand)
1388
+ end
1389
+ [s,m,e]
1390
+ end
1391
+
1392
+ def from_integral_sign_significand_exponent(s,m,e)
1393
+ msb = radix_power(@significand_digits-1)
1394
+
1395
+ if e==:zero
1396
+ e = @zero_encoded_exp
1397
+ m = 0
1398
+ elsif e==:infinity
1399
+ e = @infinite_encoded_exp || radix_power(@fields[:exponent])-1
1400
+ m = 0
1401
+ elsif e==:nan
1402
+ e = @infinite_encoded_exp || radix_power(@fields[:exponent])-1
1403
+ s = minus_sign_value # ?
1404
+ m = radix_power(@significand_digits-2) if m==0
1405
+ elsif e==:denormal
1406
+ e = @denormal_encoded_exp || @zero_encoded_exp
1407
+ else
1408
+ # to do: try to adjust m to keep e in range if out of valid range
1409
+ # to do: reduce m and adjust e if m too big
1410
+
1411
+ min_exp = radix_min_exp(:integral_significand)
1412
+ if m>0
1413
+ while m<msb && e>min_exp
1414
+ e -= 1
1415
+ m <<= 4 # m *= radix
1416
+ end
1417
+ end
1418
+ if m<msb && @denormal_encoded_exp
1419
+ e = @denormal_encoded_exp
1420
+ elsif m==0 # => && @denormal_encoded_exp.nil?
1421
+ e = 0
1422
+ else
1423
+ e = encode_exponent(e, :integral_significand)
1424
+ end
1425
+ end
1426
+ m,e = neg_significand_exponent(0,m,e) if s%2==1
1427
+ from_fields_hash :sign=>s, :significand=>m, :exponent=>e
1428
+ end
1429
+
1430
+
1431
+ def minus_sign_value
1432
+ (-1) % 2
1433
+ end
1434
+
1435
+ def exponent_digits
1436
+ @fields[exponent]/4
1437
+ end
1438
+
1439
+ # :startdoc:
1440
+
1441
+ end
1442
+
1443
+
1444
+ # A class to handle a floating-point representation (bytes) and its format class in a single object.
1445
+ # This eases the definition and manipulation of floating-point values:
1446
+ #
1447
+ # v = FltPnt::Value.from_fmt(FltPnt::IEEE_DOUBLE, '1.1')
1448
+ # # or:
1449
+ # v = FltPnt::IEEE_DOUBLE.from_fmt('1.1')
1450
+ # puts v.to_fields_hash.inspect # -> {:sign=>0, :significand=>450359962737050, :exponent=>1023}
1451
+ # puts v.next.to_hex(true) # -> 9B 99 99 99 99 99 F1 3F
1452
+ # w = v.convert_to(FltPnt::IEEE_SINGLE)
1453
+ # puts w.next.to_hex(true) # -> CE CC 8C 3F
1454
+ #
1455
+ class Value
1456
+ def initialize(fptype,value)
1457
+ @fptype = fptype
1458
+ @value = value
1459
+ end
1460
+ def self.from_fmt(fptype,txt,fmt=Nio::Fmt.default)
1461
+ self.new fptype, fptype.from_fmt(txt,fmt)
1462
+ end
1463
+ def self.from_hex(fptype,hx)
1464
+ self.new fptype, fptype.from_hex(hx)
1465
+ end
1466
+ def self.from_fields(fptype, *fields)
1467
+ self.new fptype, fptype.from_fields(*fields)
1468
+ end
1469
+ def self.from_fields_hash(fptype, fields)
1470
+ self.new fptype, fptype.from_fields_hash(fields)
1471
+ end
1472
+ def self.from_integral_sign_significand_exponent(fptype,s,m,e)
1473
+ self.new fptype, fptype.from_integral_sign_significand_exponent(s,m,e)
1474
+ end
1475
+ def self.from_fractional_sign_significand_exponent(fptype,sign,fraction,exponent)
1476
+ self.new fptype, fptype.from_fractional_sign_significand_exponent(sign,fraction,exponent)
1477
+ end
1478
+ def self.from_number(fptype,v,mode=:approx)
1479
+ self.new fptype, fptype.from_number(v,mode)
1480
+ end
1481
+ def self.from_bits_integer(i)
1482
+ self.new fptype, fptype.from_bits_integer(i)
1483
+ end
1484
+ def self.from_bits_text(txt,base)
1485
+ self.new fptype, fptype.from_bits_text(txt,base)
1486
+ end
1487
+
1488
+ def to_fmt(fmt=Nio::Fmt.default)
1489
+ @fptype.to_fmt(@value,fmt)
1490
+ end
1491
+ def to_hex(sep_bytes=false)
1492
+ @fptype.to_hex(@value,sep_bytes)
1493
+ end
1494
+ def to_fields
1495
+ @fptype.to_fields(@value)
1496
+ end
1497
+ def to_fields_hash
1498
+ @fptype.to_fields_hash(@value)
1499
+ end
1500
+ def to_integral_sign_significand_exponent
1501
+ @fptype.to_integral_sign_significand_exponent(@value)
1502
+ end
1503
+ def to_fractional_sign_significand_exponent(significand_mode=:normalized_significand)
1504
+ @fptype.to_fractional_sign_significand_exponent(@value, significand_mode)
1505
+ end
1506
+ def to_number(number_class, mode=:approx)
1507
+ @fptype.to_number(@value, number_class, mode)
1508
+ end
1509
+ def to_bits_integer
1510
+ @fptype.to_bits_integer @value
1511
+ end
1512
+ def to_bits_text(base)
1513
+ @fptype.to_bits_text @value, base
1514
+ end
1515
+
1516
+ def convert_to(fptype2)
1517
+ self.class.new fptype2, fptype2.from_fmt(to_fmt)
1518
+ end
1519
+
1520
+ def next
1521
+ self.class.new(@fptype, @fptype.next_float(@value))
1522
+ end
1523
+ def prev
1524
+ self.class.new(@fptype, @fptype.prev_float(@value))
1525
+ end
1526
+
1527
+ def fp_format
1528
+ @fptype
1529
+ end
1530
+ def bytes
1531
+ @value
1532
+ end
1533
+
1534
+ def ==(obj); test_equal(obj); end
1535
+ def eql?(obj); test_equal(obj); end
1536
+ def ===(obj); test_equal(obj); end
1537
+
1538
+ private
1539
+ def test_equal(v)
1540
+ @fptype==v.fp_format && @value==v.bytes
1541
+ end
1542
+
1543
+
1544
+ end
1545
+
1546
+
1547
+
1548
+
1549
+ end
1550
+