flt 1.0.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.
Files changed (56) hide show
  1. data/History.txt +41 -0
  2. data/License.txt +20 -0
  3. data/Manifest.txt +42 -0
  4. data/README.txt +557 -0
  5. data/Rakefile +34 -0
  6. data/lib/flt.rb +9 -0
  7. data/lib/flt/b.rb +6 -0
  8. data/lib/flt/bigdecimal.rb +151 -0
  9. data/lib/flt/bin_num.rb +250 -0
  10. data/lib/flt/d.rb +6 -0
  11. data/lib/flt/dec_num.rb +1239 -0
  12. data/lib/flt/float.rb +458 -0
  13. data/lib/flt/math.rb +66 -0
  14. data/lib/flt/num.rb +4211 -0
  15. data/lib/flt/sugar.rb +102 -0
  16. data/lib/flt/support.rb +1335 -0
  17. data/lib/flt/tolerance.rb +561 -0
  18. data/lib/flt/tolerance/sugar.rb +77 -0
  19. data/lib/flt/version.rb +9 -0
  20. data/setup.rb +1585 -0
  21. data/tasks/ann.rake +80 -0
  22. data/tasks/bones.rake +20 -0
  23. data/tasks/gem.rake +192 -0
  24. data/tasks/git.rake +40 -0
  25. data/tasks/manifest.rake +48 -0
  26. data/tasks/notes.rake +27 -0
  27. data/tasks/post_load.rake +39 -0
  28. data/tasks/rdoc.rake +50 -0
  29. data/tasks/rubyforge.rake +55 -0
  30. data/tasks/setup.rb +279 -0
  31. data/tasks/spec.rake +54 -0
  32. data/tasks/svn.rake +47 -0
  33. data/tasks/test.rake +40 -0
  34. data/test/all_tests.rb +23 -0
  35. data/test/helper.rb +101 -0
  36. data/test/reader.rb +68 -0
  37. data/test/test_basic.rb +396 -0
  38. data/test/test_bin.rb +245 -0
  39. data/test/test_bin_arithmetic.rb +94 -0
  40. data/test/test_binfloat_conversion.rb +24 -0
  41. data/test/test_coercion.rb +22 -0
  42. data/test/test_comparisons.rb +53 -0
  43. data/test/test_dectest.rb +216 -0
  44. data/test/test_define_conversions.rb +144 -0
  45. data/test/test_epsilon.rb +55 -0
  46. data/test/test_exact.rb +147 -0
  47. data/test/test_flags.rb +34 -0
  48. data/test/test_multithreading.rb +32 -0
  49. data/test/test_num_constructor.rb +133 -0
  50. data/test/test_odd_even.rb +78 -0
  51. data/test/test_round.rb +104 -0
  52. data/test/test_to_int.rb +104 -0
  53. data/test/test_to_rf.rb +36 -0
  54. data/test/test_tol.rb +102 -0
  55. data/test/test_ulp.rb +127 -0
  56. metadata +147 -0
data/lib/flt/sugar.rb ADDED
@@ -0,0 +1,102 @@
1
+ # Optional (and intrusive) shortcuts for numeric types
2
+ #
3
+ # require 'flt/sugar'
4
+ #
5
+ # puts 0.1.split.inspect
6
+ # puts 0.1.sqrt
7
+ # puts 0.1.next_plus
8
+ #
9
+ # puts 11.odd?
10
+ # puts 11.even?
11
+ # puts 11.sign
12
+ # puts 0.sign
13
+ # puts (-11).sign
14
+ #
15
+ # puts 11.0.odd?
16
+ # puts 11.0.even?
17
+ # puts 11.0.sign
18
+ # puts 0.0.sign
19
+ # puts (-0.0).sign
20
+ # puts (-11.0).sign
21
+ #
22
+ # puts Rational(11,3).split.inspect
23
+ #
24
+ # puts BigDecimal::Math.sin(BigDecimal('0.1'), 20)
25
+ # include BigDecimal::Math
26
+ # puts sin(BigDecimal('0.1'), 20)
27
+ #
28
+
29
+ require 'flt/float'
30
+ require 'flt/bigdecimal'
31
+ require 'flt/d'
32
+ require 'flt/b'
33
+
34
+ class Float
35
+
36
+ def self.radix
37
+ context.radix
38
+ end
39
+
40
+ def self.Num(*args)
41
+ context.Num(*args)
42
+ end
43
+
44
+ class <<self
45
+ def _sugar_context_method(*methods) #:nodoc:
46
+ methods.each do |method|
47
+ define_method(method) do
48
+ Float.context.send(method, self)
49
+ end
50
+ end
51
+ end
52
+
53
+ def _sugar_math_method(*methods) #:nodoc:
54
+ methods.each do |method|
55
+ define_method(method) do
56
+ Math.send(method, self)
57
+ end
58
+ end
59
+ end
60
+ end
61
+
62
+ _sugar_context_method :split, :to_int_scale, :next_plus, :next_minus, :sign,
63
+ :special?, :subnormal?, :normal?
64
+ _sugar_math_method :sqrt, :log, :log10, :exp
65
+
66
+ def next_toward(other)
67
+ Float.context.next_toward(self, other)
68
+ end
69
+
70
+ end
71
+
72
+ class Numeric
73
+
74
+ def even?
75
+ self.modulo(2) == 0
76
+ end
77
+
78
+ def odd?
79
+ self.modulo(2) == 1
80
+ end
81
+
82
+ def sign
83
+ self < 0 ? -1 : +1
84
+ end
85
+
86
+ end
87
+
88
+ class Rational
89
+
90
+ def split
91
+ [numerator, denominator]
92
+ end
93
+
94
+ end
95
+
96
+ module BigDecimal::Math
97
+ include BigMath
98
+ instance_methods.each do |method|
99
+ module_function method
100
+ end
101
+ end
102
+
@@ -0,0 +1,1335 @@
1
+ module Flt
2
+ module Support
3
+ # This class assigns bit-values to a set of symbols
4
+ # so they can be used as flags and stored as an integer.
5
+ # fv = FlagValues.new(:flag1, :flag2, :flag3)
6
+ # puts fv[:flag3]
7
+ # fv.each{|f,v| puts "#{f} -> #{v}"}
8
+ class FlagValues
9
+
10
+ #include Enumerator
11
+
12
+ class InvalidFlagError < StandardError
13
+ end
14
+ class InvalidFlagTypeError < StandardError
15
+ end
16
+
17
+
18
+ # The flag symbols must be passed; values are assign in increasing order.
19
+ # fv = FlagValues.new(:flag1, :flag2, :flag3)
20
+ # puts fv[:flag3]
21
+ def initialize(*flags)
22
+ @flags = {}
23
+ value = 1
24
+ flags.each do |flag|
25
+ raise InvalidFlagType,"Flags must be defined as symbols or classes; invalid flag: #{flag.inspect}" unless flag.kind_of?(Symbol) || flag.instance_of?(Class)
26
+ @flags[flag] = value
27
+ value <<= 1
28
+ end
29
+ end
30
+
31
+ # Get the bit-value of a flag
32
+ def [](flag)
33
+ v = @flags[flag]
34
+ raise InvalidFlagError, "Invalid flag: #{flag}" unless v
35
+ v
36
+ end
37
+
38
+ # Return each flag and its bit-value
39
+ def each(&blk)
40
+ if blk.arity==2
41
+ @flags.to_a.sort_by{|f,v|v}.each(&blk)
42
+ else
43
+ @flags.to_a.sort_by{|f,v|v}.map{|f,v|f}.each(&blk)
44
+ end
45
+ end
46
+
47
+ def size
48
+ @flags.size
49
+ end
50
+
51
+ def all_flags_value
52
+ (1 << size) - 1
53
+ end
54
+
55
+ end
56
+
57
+ # This class stores a set of flags. It can be assign a FlagValues
58
+ # object (using values= or passing to the constructor) so that
59
+ # the flags can be store in an integer (bits).
60
+ class Flags
61
+
62
+ class Error < StandardError
63
+ end
64
+ class InvalidFlagError < Error
65
+ end
66
+ class InvalidFlagValueError < Error
67
+ end
68
+ class InvalidFlagTypeError < Error
69
+ end
70
+
71
+ # When a Flag object is created, the initial flags to be set can be passed,
72
+ # and also a FlagValues. If a FlagValues is passed an integer can be used
73
+ # to define the flags.
74
+ # Flags.new(:flag1, :flag3, FlagValues.new(:flag1,:flag2,:flag3))
75
+ # Flags.new(5, FlagValues.new(:flag1,:flag2,:flag3))
76
+ def initialize(*flags)
77
+ @values = nil
78
+ @flags = {}
79
+
80
+ v = 0
81
+
82
+ flags.flatten!
83
+
84
+ flags.each do |flag|
85
+ case flag
86
+ when FlagValues
87
+ @values = flag
88
+ when Symbol, Class
89
+ @flags[flag] = true
90
+ when Integer
91
+ v |= flag
92
+ when Flags
93
+ @values = flag.values
94
+ @flags = flag.to_h.dup
95
+ else
96
+ raise InvalidFlagTypeError, "Invalid flag type for: #{flag.inspect}"
97
+ end
98
+ end
99
+
100
+ if v!=0
101
+ raise InvalidFlagTypeError, "Integer flag values need flag bit values to be defined" if @values.nil?
102
+ self.bits = v
103
+ end
104
+
105
+ if @values
106
+ # check flags
107
+ @flags.each_key{|flag| check flag}
108
+ end
109
+
110
+ end
111
+
112
+ def dup
113
+ Flags.new(self)
114
+ end
115
+
116
+ # Clears all flags
117
+ def clear!
118
+ @flags = {}
119
+ end
120
+
121
+ # Sets all flags
122
+ def set!
123
+ if @values
124
+ self.bits = @values.all_flags_value
125
+ else
126
+ raise Error,"No flag values defined"
127
+ end
128
+ end
129
+
130
+ # Assign the flag bit values
131
+ def values=(fv)
132
+ @values = fv
133
+ end
134
+
135
+ # Retrieves the flag bit values
136
+ def values
137
+ @values
138
+ end
139
+
140
+ # Retrieves the flags as a bit-vector integer. Values must have been assigned.
141
+ def bits
142
+ if @values
143
+ i = 0
144
+ @flags.each do |f,v|
145
+ bit_val = @values[f]
146
+ i |= bit_val if v && bit_val
147
+ end
148
+ i
149
+ else
150
+ raise Error,"No flag values defined"
151
+ end
152
+ end
153
+
154
+ # Sets the flags as a bit-vector integer. Values must have been assigned.
155
+ def bits=(i)
156
+ if @values
157
+ raise Error, "Invalid bits value #{i}" if i<0 || i>@values.all_flags_value
158
+ clear!
159
+ @values.each do |f,v|
160
+ @flags[f]=true if (i & v)!=0
161
+ end
162
+ else
163
+ raise Error,"No flag values defined"
164
+ end
165
+ end
166
+
167
+ # Retrieves the flags as a hash.
168
+ def to_h
169
+ @flags
170
+ end
171
+
172
+ # Same as bits
173
+ def to_i
174
+ bits
175
+ end
176
+
177
+ # Retrieve the setting (true/false) of a flag
178
+ def [](flag)
179
+ check flag
180
+ @flags[flag]
181
+ end
182
+
183
+ # Modifies the setting (true/false) of a flag.
184
+ def []=(flag,value)
185
+ check flag
186
+ case value
187
+ when true,1
188
+ value = true
189
+ when false,0,nil
190
+ value = false
191
+ else
192
+ raise InvalidFlagValueError, "Invalid value: #{value.inspect}"
193
+ end
194
+ @flags[flag] = value
195
+ value
196
+ end
197
+
198
+ # Sets (makes true) one or more flags
199
+ def set(*flags)
200
+ flags = flags.first if flags.size==1 && flags.first.instance_of?(Array)
201
+ flags.each do |flag|
202
+ if flag.kind_of?(Flags)
203
+ #if @values && other.values && compatible_values(other_values)
204
+ # self.bits |= other.bits
205
+ #else
206
+ flags.concat other.to_a
207
+ #end
208
+ else
209
+ check flag
210
+ @flags[flag] = true
211
+ end
212
+ end
213
+ end
214
+
215
+ # Clears (makes false) one or more flags
216
+ def clear(*flags)
217
+ flags = flags.first if flags.size==1 && flags.first.instance_of?(Array)
218
+ flags.each do |flag|
219
+ if flag.kind_of?(Flags)
220
+ #if @values && other.values && compatible_values(other_values)
221
+ # self.bits &= ~other.bits
222
+ #else
223
+ flags.concat other.to_a
224
+ #end
225
+ else
226
+ check flag
227
+ @flags[flag] = false
228
+ end
229
+ end
230
+ end
231
+
232
+ # Sets (makes true) one or more flags (passes as an array)
233
+ def << (flags)
234
+ if flags.kind_of?(Array)
235
+ set(*flags)
236
+ else
237
+ set(flags)
238
+ end
239
+ end
240
+
241
+ # Iterate on each flag/setting pair.
242
+ def each(&blk)
243
+ if @values
244
+ @values.each do |f,v|
245
+ blk.call(f,@flags[f])
246
+ end
247
+ else
248
+ @flags.each(&blk)
249
+ end
250
+ end
251
+
252
+ # Iterate on each set flag
253
+ def each_set
254
+ each do |f,v|
255
+ yield f if v
256
+ end
257
+ end
258
+
259
+ # Iterate on each cleared flag
260
+ def each_clear
261
+ each do |f,v|
262
+ yield f if !v
263
+ end
264
+ end
265
+
266
+ # returns true if any flag is set
267
+ def any?
268
+ if @values
269
+ bits != 0
270
+ else
271
+ to_a.size>0
272
+ end
273
+ end
274
+
275
+ # Returns the true flags as an array
276
+ def to_a
277
+ a = []
278
+ each_set{|f| a << f}
279
+ a
280
+ end
281
+
282
+ def to_s
283
+ "[#{to_a.map{|f| f.to_s.split('::').last}.join(', ')}]"
284
+ end
285
+
286
+ def inspect
287
+ txt = "#{self.class.to_s}#{to_s}"
288
+ txt << " (0x#{bits.to_s(16)})" if @values
289
+ txt
290
+ end
291
+
292
+
293
+ def ==(other)
294
+ if @values && other.values && compatible_values?(other.values)
295
+ bits == other.bits
296
+ else
297
+ to_a.map{|s| s.to_s}.sort == other.to_a.map{|s| s.to_s}.sort
298
+ end
299
+ end
300
+
301
+
302
+
303
+ private
304
+ def check(flag)
305
+ raise InvalidFlagType,"Flags must be defined as symbols or classes; invalid flag: #{flag.inspect}" unless flag.kind_of?(Symbol) || flag.instance_of?(Class)
306
+
307
+ @values[flag] if @values # raises an invalid flag error if flag is invalid
308
+ true
309
+ end
310
+
311
+ def compatible_values?(v)
312
+ #@values.object_id==v.object_id
313
+ @values == v
314
+ end
315
+
316
+ end
317
+
318
+ module_function
319
+
320
+ # Constructor for FlagValues
321
+ def FlagValues(*params)
322
+ if params.size==1 && params.first.kind_of?(FlagValues)
323
+ params.first
324
+ else
325
+ FlagValues.new(*params)
326
+ end
327
+ end
328
+
329
+ # Constructor for Flags
330
+ def Flags(*params)
331
+ if params.size==1 && params.first.kind_of?(Flags)
332
+ params.first
333
+ else
334
+ Flags.new(*params)
335
+ end
336
+ end
337
+
338
+ module_function
339
+ # replace :ceiling and :floor rounding modes by :up/:down (depending on sign of the number to be rounded)
340
+ def simplified_round_mode(round_mode, negative)
341
+ if negative
342
+ if round_mode == :ceiling
343
+ round_mode = :floor
344
+ elsif round_mode == :floor
345
+ round_mode = :ceiling
346
+ end
347
+ end
348
+ if round_mode == :ceiling
349
+ round_mode = :up
350
+ elsif round_mode == :floor
351
+ round_mode = :down
352
+ end
353
+ round_mode
354
+ end
355
+
356
+
357
+ # Floating-point reading and printing (from/to text literals).
358
+ #
359
+ # Here are methods for floating-point reading using algorithms by William D. Clinger and
360
+ # printing using algorithms by Robert G. Burger and R. Kent Dybvig.
361
+ #
362
+ # Reading and printing can also viewed as floating-point conversion betwen a fixed-precision
363
+ # floating-point format (the floating-point numbers) and and a free floating-point format (text) which
364
+ # may use different numerical bases.
365
+ #
366
+ # The Reader class implements, in the default :free mode, converts a free-form numeric value
367
+ # (as a text literal, i.e. a free floating-point format, usually in base 10) which is taken
368
+ # as an exact value, to a correctly-rounded floating-point of specified precision and with a
369
+ # specified rounding mode. It also has a :fixed mode that uses the Formatter class indirectly.
370
+ #
371
+ # The Formatter class implements the Burger-Dybvig printing algorithm which converts a
372
+ # fixed-precision floating point value and produces a text literal in same base, usually 10,
373
+ # (equivalently, it produces a floating-point free-format value) so that it rounds back to
374
+ # the original value (with some specified rounding-mode or any round-to-nearest mode) and with
375
+ # the same original precision (e.g. using the Clinger algorithm)
376
+
377
+ # Clinger algorithms to read floating point numbers from text literals with correct rounding.
378
+ # from his paper: "How to Read Floating Point Numbers Accurately"
379
+ # (William D. Clinger)
380
+ class Reader
381
+
382
+ # There are two different reading approaches, selected by the :mode parameter:
383
+ # * :fixed (the destination context defines the resulting precision) input is rounded as specified
384
+ # by the context; if the context precision is 'exact', the exact input value will be represented
385
+ # in the destination base, which can lead to a Inexact exception (or a NaN result and an Inexact flag)
386
+ # * :free The input precision is preserved, and the destination context precision is ignored;
387
+ # in this case the result can be converted back to the original number (with the same precision)
388
+ # a rounding mode for the back conversion may be passed; otherwise any round-to-nearest is assumed.
389
+ # (to increase the precision of the result the input precision must be increased --adding trailing zeros)
390
+ # * :short is like :free, but the minumum number of digits that preserve the original value
391
+ # are generated (with :free, all significant digits are generated)
392
+ #
393
+ # For the fixed mode there are three conversion algorithms available that can be selected with the
394
+ # :algorithm parameter:
395
+ # * :A Arithmetic algorithm, using correctly rounded Flt::Num arithmetic.
396
+ # * :M The Clinger Algorithm M is the slowest method, but it was the first implemented and testes and
397
+ # is kept as a reference for testing.
398
+ # * :R The Clinger Algorithm R, which requires an initial approximation is currently only implemented
399
+ # for Float and is the fastest by far.
400
+ def initialize(options={})
401
+ @exact = nil
402
+ @algorithm = options[:algorithm]
403
+ @mode = options[:mode] || :fixed
404
+ end
405
+
406
+ def exact?
407
+ @exact
408
+ end
409
+
410
+ # Given exact integers f and e, with f nonnegative, returns the floating-point number
411
+ # closest to f * eb**e
412
+ # (eb is the input radix)
413
+ #
414
+ # If the context precision is exact an Inexact exception may occur (an NaN be returned)
415
+ # if an exact conversion is not possible.
416
+ #
417
+ # round_mode: in :fixed mode it specifies how to round the result (to the context precision); it
418
+ # is passed separate from context for flexibility.
419
+ # in :free mode it specifies what rounding would be used to convert back the output to the
420
+ # input base eb (using the same precision that f has).
421
+ def read(context, round_mode, sign, f, e, eb=10)
422
+ @exact = true
423
+
424
+ case @mode
425
+ when :free, :short
426
+ all_digits = (@mode == :free)
427
+ # for free mode, (any) :nearest rounding is used by default
428
+ Num.convert(Num[eb].Num(sign, f, e), context.num_class, :rounding=>round_mode||:nearest, :all_digits=>all_digits)
429
+ when :fixed
430
+ if exact_mode = context.exact?
431
+ a,b = [eb, context.radix].sort
432
+ m = (Math.log(b)/Math.log(a)).round
433
+ if b == a**m
434
+ # conmensurable bases
435
+ if eb > context.radix
436
+ n = AuxiliarFunctions._ndigits(f, eb)*m
437
+ else
438
+ n = (AuxiliarFunctions._ndigits(f, eb)+m-1)/m
439
+ end
440
+ else
441
+ # inconmesurable bases; exact result may not be possible
442
+ x = Num[eb].Num(sign, f, e)
443
+ x = Num.convert_exact(x, context.num_class, context)
444
+ @exact = !x.nan?
445
+ return x
446
+ end
447
+ else
448
+ n = context.precision
449
+ end
450
+ if round_mode == :nearest
451
+ # :nearest is not meaningful here in :fixed mode; replace it
452
+ if [:half_even, :half_up, :half_down].include?(context.rounding)
453
+ round_mode = context.rounding
454
+ else
455
+ round_mode = :half_even
456
+ end
457
+ end
458
+ # for fixed mode, use the context rounding by default
459
+ round_mode ||= context.rounding
460
+ alg = @algorithm
461
+ if (context.radix == 2 && alg.nil?) || alg==:R
462
+ z0 = _alg_r_approx(context, round_mode, sign, f, e, eb, n)
463
+ alg = z0 && :R
464
+ end
465
+ alg ||= :A
466
+ case alg
467
+ when :M, :R
468
+ round_mode = Support.simplified_round_mode(round_mode, sign == -1)
469
+ case alg
470
+ when :M
471
+ _alg_m(context, round_mode, sign, f, e, eb, n)
472
+ when :R
473
+ _alg_r(z0, context, round_mode, sign, f, e, eb, n)
474
+ end
475
+ else # :A
476
+ # direct arithmetic conversion
477
+ if round_mode == context.rounding
478
+ x = Num.convert_exact(Num[eb].Num(sign, f, e), context.num_class, context)
479
+ x = context.normalize(x) unless !context.respond_to?(:normalize) || context.exact?
480
+ x
481
+ else
482
+ if context.num_class == Float
483
+ float = true
484
+ context = BinNum::FloatContext
485
+ end
486
+ x = context.num_class.context(context) do |context|
487
+ context.rounding = round_mode
488
+ Num.convert_exact(Num[eb].Num(sign, f, e), context.num_class, context)
489
+ end
490
+ if float
491
+ x = x.to_f
492
+ else
493
+ x = context.normalize(x) unless context.exact?
494
+ end
495
+ x
496
+ end
497
+ end
498
+ end
499
+ end
500
+
501
+ def _alg_r_approx(context, round_mode, sign, f, e, eb, n)
502
+
503
+ return nil if context.radix != Float::RADIX || context.exact? || context.precision > Float::MANT_DIG
504
+
505
+ # Compute initial approximation; if Float uses IEEE-754 binary arithmetic, the approximation
506
+ # is good enough to be adjusted in just one step.
507
+ @good_approx = true
508
+
509
+ ndigits = Support::AuxiliarFunctions._ndigits(f, eb)
510
+ adj_exp = e + ndigits - 1
511
+ min_exp, max_exp = Reader.float_min_max_adj_exp(eb)
512
+
513
+ if adj_exp >= min_exp && adj_exp <= max_exp
514
+ if eb==2
515
+ z0 = Math.ldexp(f,e)
516
+ elsif eb==10
517
+ unless Flt.float_correctly_rounded?
518
+ min_exp_norm, max_exp_norm = Reader.float_min_max_adj_exp(eb, true)
519
+ @good_approx = false
520
+ return nil if e <= min_exp_norm
521
+ end
522
+ z0 = Float("#{f}E#{e}")
523
+ else
524
+ ff = f
525
+ ee = e
526
+ min_exp_norm, max_exp_norm = Reader.float_min_max_adj_exp(eb, true)
527
+ if e <= min_exp_norm
528
+ # avoid loss of precision due to gradual underflow
529
+ return nil if e <= min_exp
530
+ @good_approx = false
531
+ ff = Float(f)*Float(eb)**(e-min_exp_norm-1)
532
+ ee = min_exp_norm + 1
533
+ end
534
+ # if ee < 0
535
+ # z0 = Float(ff)/Float(eb**(-ee))
536
+ # else
537
+ # z0 = Float(ff)*Float(eb**ee)
538
+ # end
539
+ z0 = Float(ff)*Float(eb)**ee
540
+ end
541
+
542
+ if z0 && context.num_class != Float
543
+ @good_approx = false
544
+ z0 = context.Num(z0).plus(context) # context.plus(z0) ?
545
+ else
546
+ z0 = context.Num(z0)
547
+ end
548
+ end
549
+
550
+ end
551
+
552
+ def _alg_r(z0, context, round_mode, sign, f, e, eb, n) # Fast for Float
553
+ #raise InvalidArgument, "Reader Algorithm R only supports base 2" if context.radix != 2
554
+
555
+ @z = z0
556
+ @r = context.radix
557
+ @rp_n_1 = context.int_radix_power(n-1)
558
+ @round_mode = round_mode
559
+
560
+ ret = nil
561
+ loop do
562
+ m, k = context.to_int_scale(@z)
563
+ # TODO: replace call to compare by setting the parameters in local variables,
564
+ # then insert the body of compare here;
565
+ # then eliminate innecesary instance variables
566
+ if e >= 0 && k >= 0
567
+ ret = compare m, f*eb**e, m*@r**k, context
568
+ elsif e >= 0 && k < 0
569
+ ret = compare m, f*eb**e*@r**(-k), m, context
570
+ elsif e < 0 && k >= 0
571
+ ret = compare m, f, m*@r**k*eb**(-e), context
572
+ else # e < 0 && k < 0
573
+ ret = compare m, f*@r**(-k), m*eb**(-e), context
574
+ end
575
+ break if ret
576
+ end
577
+ ret && context.copy_sign(ret, sign) # TODO: normalize?
578
+ end
579
+
580
+ @float_min_max_exp_values = {
581
+ 10 => [Float::MIN_10_EXP, Float::MAX_10_EXP],
582
+ Float::RADIX => [Float::MIN_EXP, Float::MAX_EXP],
583
+ -Float::RADIX => [Float::MIN_EXP-Float::MANT_DIG, Float::MAX_EXP-Float::MANT_DIG]
584
+ }
585
+ class <<self
586
+ # Minimum & maximum adjusted exponent for numbers in base to be in the range of Floats
587
+ def float_min_max_adj_exp(base, normalized=false)
588
+ k = normalized ? base : -base
589
+ unless min_max = @float_min_max_exp_values[k]
590
+ max_exp = (Math.log(Float::MAX)/Math.log(base)).floor
591
+ e = Float::MIN_EXP
592
+ e -= Float::MANT_DIG unless normalized
593
+ min_exp = (e*Math.log(Float::RADIX)/Math.log(base)).ceil
594
+ @float_min_max_exp_values[k] = min_max = [min_exp, max_exp]
595
+ end
596
+ min_max.map{|e| e - 1} # adjust
597
+ end
598
+ end
599
+
600
+ def compare(m, x, y, context)
601
+ ret = nil
602
+ d = x-y
603
+ d2 = 2*m*d.abs
604
+
605
+ # v = f*eb**e is the number to be approximated
606
+ # z = m*@r**k is the current aproximation
607
+ # the error of @z is eps = abs(v-z) = 1/2 * d2 / y
608
+ # we have x, y integers such that x/y = v/z
609
+ # so eps < 1/2 <=> d2 < y
610
+ # d < 0 <=> x < y <=> v < z
611
+
612
+ directed_rounding = [:up, :down].include?(@round_mode)
613
+
614
+ if directed_rounding
615
+ if @round_mode==:up ? (d <= 0) : (d < 0)
616
+ # v <(=) z
617
+ chk = (m == @rp_n_1) ? d2*@r : d2
618
+ if (@round_mode == :up) && (chk < 2*y)
619
+ # eps < 1
620
+ ret = @z
621
+ else
622
+ @z = context.next_minus(@z)
623
+ end
624
+ else # @round_mode==:up ? (d > 0) : (d >= 0)
625
+ # v >(=) z
626
+ if (@round_mode == :down) && (d2 < 2*y)
627
+ # eps < 1
628
+ ret = @z
629
+ else
630
+ @z = context.next_plus(@z)
631
+ end
632
+ end
633
+ else
634
+ if d2 < y # eps < 1/2
635
+ if (m == @rp_n_1) && (d < 0) && (y < @r*d2)
636
+ # z has the minimum normalized significand, i.e. is a power of @r
637
+ # and v < z
638
+ # and @r*eps > 1/2
639
+ # On the left of z the ulp is 1/@r than the ulp on the right; if v < z we
640
+ # must require an error @r times smaller.
641
+ @z = context.next_minus(@z)
642
+ else
643
+ # unambiguous nearest
644
+ ret = @z
645
+ end
646
+ elsif d2 == y # eps == 1/2
647
+ # round-to-nearest tie
648
+ if @round_mode == :half_even
649
+ if (m%2) == 0
650
+ # m is even
651
+ if (m == @rp_n_1) && (d < 0)
652
+ # z is power of @r and v < z; this wasn't really a tie because
653
+ # there are closer values on the left
654
+ @z = context.next_minus(@z)
655
+ else
656
+ # m is even => round tie to z
657
+ ret = @z
658
+ end
659
+ elsif d < 0
660
+ # m is odd, v < z => round tie to prev
661
+ ret = context.next_minus(@z)
662
+ elsif d > 0
663
+ # m is odd, v > z => round tie to next
664
+ ret = context.next_plus(@z)
665
+ end
666
+ elsif @round_mode == :half_up
667
+ if d < 0
668
+ # v < z
669
+ if (m == @rp_n_1)
670
+ # this was not really a tie
671
+ @z = context.next_minus(@z)
672
+ else
673
+ ret = @z
674
+ end
675
+ else # d > 0
676
+ # v >= z
677
+ ret = context.next_plus(@z)
678
+ end
679
+ else # @round_mode == :half_down
680
+ if d < 0
681
+ # v < z
682
+ if (m == @rp_n_1)
683
+ # this was not really a tie
684
+ @z = context.next_minus(@z)
685
+ else
686
+ ret = context.next_minus(@z)
687
+ end
688
+ else # d < 0
689
+ # v > z
690
+ ret = @z
691
+ end
692
+ end
693
+ elsif d < 0 # eps > 1/2 and v < z
694
+ @z = context.next_minus(@z)
695
+ elsif d > 0 # eps > 1/2 and v > z
696
+ @z = context.next_plus(@z)
697
+ end
698
+ end
699
+
700
+ # Assume the initial approx is good enough (uses IEEE-754 arithmetic with round-to-nearest),
701
+ # so we can avoid further iteration, except for directed rounding
702
+ ret ||= @z unless directed_rounding || !@good_approx
703
+
704
+ return ret
705
+ end
706
+
707
+ # Algorithm M to read floating point numbers from text literals with correct rounding
708
+ # from his paper: "How to Read Floating Point Numbers Accurately" (William D. Clinger)
709
+ def _alg_m(context, round_mode, sign, f, e, eb, n)
710
+ if e<0
711
+ u,v,k = f,eb**(-e),0
712
+ else
713
+ u,v,k = f*(eb**e),1,0
714
+ end
715
+ min_e = context.etiny
716
+ max_e = context.etop
717
+ rp_n = context.int_radix_power(n)
718
+ rp_n_1 = context.int_radix_power(n-1)
719
+ r = context.radix
720
+ loop do
721
+ x = u.div(v) # bottleneck
722
+ if (x>=rp_n_1 && x<rp_n) || k==min_e || k==max_e
723
+ z, exact = Reader.ratio_float(context,u,v,k,round_mode)
724
+ @exact = exact
725
+ if context.respond_to?(:exception)
726
+ if k==min_e
727
+ context.exception(Num::Subnormal) if z.subnormal?
728
+ context.exception(Num::Underflow,"Input literal out of range") if z.zero? && f!=0
729
+ elsif k==max_e
730
+ if !context.exact? && z.coefficient > context.maximum_coefficient
731
+ context.exception(Num::Overflow,"Input literal out of range")
732
+ end
733
+ end
734
+ context.exception Num::Inexact if !exact
735
+ end
736
+ return z.copy_sign(sign)
737
+ elsif x<rp_n_1
738
+ u *= r
739
+ k -= 1
740
+ elsif x>=rp_n
741
+ v *= r
742
+ k += 1
743
+ end
744
+ end
745
+ end
746
+
747
+ # Given exact positive integers u and v with beta**(n-1) <= u/v < beta**n
748
+ # and exact integer k, returns the floating point number closest to u/v * beta**n
749
+ # (beta is the floating-point radix)
750
+ def self.ratio_float(context, u, v, k, round_mode)
751
+ # since this handles only positive numbers and ceiling and floor
752
+ # are not symmetrical, they should have been swapped before calling this.
753
+ q = u.div v
754
+ r = u-q*v
755
+ v_r = v-r
756
+ z = context.Num(+1,q,k)
757
+ exact = (r==0)
758
+ if round_mode == :down
759
+ # z = z
760
+ elsif (round_mode == :up) && r>0
761
+ z = context.next_plus(z)
762
+ elsif r<v_r
763
+ # z = z
764
+ elsif r>v_r
765
+ z = context.next_plus(z)
766
+ else
767
+ # tie
768
+ if (round_mode == :half_down) || (round_mode == :half_even && ((q%2)==0)) || (round_mode == :down)
769
+ # z = z
770
+ else
771
+ z = context.next_plus(z)
772
+ end
773
+ end
774
+ return z, exact
775
+ end
776
+
777
+ end # Reader
778
+
779
+ # Burger and Dybvig free formatting algorithm,
780
+ # from their paper: "Printing Floating-Point Numbers Quickly and Accurately"
781
+ # (Robert G. Burger, R. Kent Dybvig)
782
+ #
783
+ # This algorithm formats arbitrary base floating point numbers as decimal
784
+ # text literals. The floating-point (with fixed precision) is interpreted as an approximated
785
+ # value, representing any value in its 'rounding-range' (the interval where all values round
786
+ # to the floating-point value, with the given precision and rounding mode).
787
+ # An alternative approach which is not taken here would be to represent the exact floating-point
788
+ # value with some given precision and rounding mode requirements; that can be achieved with
789
+ # Clinger algorithm (which may fail for exact precision).
790
+ #
791
+ # The variables used by the algorithm are stored in instance variables:
792
+ # @v - The number to be formatted = @f*@b**@e
793
+ # @b - The numberic base of the input floating-point representation of @v
794
+ # @f - The significand or characteristic (fraction)
795
+ # @e - The exponent
796
+ #
797
+ # Quotients of integers will be used to hold the magnitudes:
798
+ # @s is the denominator of all fractions
799
+ # @r numerator of @v: @v = @r/@s
800
+ # @m_m numerator of the distance from the rounding-range lower limit, l, to @v: @m_m/@s = (@v - l)
801
+ # @m_p numerator of the distance from @v to the rounding-range upper limit, u: @m_p/@s = (u - @v)
802
+ # All numbers in the randound-range are rounded to @v (with the given precision p)
803
+ # @k scale factor that is applied to the quotients @r/@s, @m_m/@s and @m_p/@s to put the first
804
+ # significant digit right after the radix point. @b**@k is the first power of @b >= u
805
+ #
806
+ # The rounding range of @v is the interval of values that round to @v under the runding-mode.
807
+ # If the rounding mode is one of the round-to-nearest variants (even, up, down), then
808
+ # it is ((v+v-)/2 = (@v-@m_m)/@s, (v+v+)/2 = (@v+@m_)/2) whith the boundaries open or closed as explained below.
809
+ # In this case:
810
+ # @m_m/@s = (@v - (v + v-)/2) where v- = @v.next_minus is the lower adjacent to v floating point value
811
+ # @m_p/@s = ((v + v+)/2 - @v) where v+ = @v.next_plus is the upper adjacent to v floating point value
812
+ # If the rounding is directed, then the rounding interval is either (v-, @v] or [@v, v+]
813
+ # @roundl is true if the lower limit of the rounding range is closed (i.e., if l rounds to @v)
814
+ # @roundh is true if the upper limit of the rounding range is closed (i.e., if u rounds to @v)
815
+ # if @roundh, then @k is the minimum @k with (@r+@m_p)/@s <= @output_b**@k
816
+ # @k = ceil(logB((@r+@m_p)/2)) with lobB the @output_b base logarithm
817
+ # if @roundh, then @k is the minimum @k with (@r+@m_p)/@s < @output_b**@k
818
+ # @k = 1+floor(logB((@r+@m_p)/2))
819
+ #
820
+ # @output_b is the output base
821
+ # @output_min_e is the output minimum exponent
822
+ # p is the input floating point precision
823
+ class Formatter
824
+
825
+ # This Object-oriented implementation is slower than the functional one for two reasons:
826
+ # * The overhead of object creation
827
+ # * The use of instance variables instead of local variables
828
+ # But if scale is optimized or local variables are used in the inner loops, then this implementation
829
+ # is on par with the functional one for Float and it is more efficient for Flt types, where the variables
830
+ # passed as parameters hold larger objects.
831
+
832
+ def initialize(input_b, input_min_e, output_b)
833
+ @b = input_b
834
+ @min_e = input_min_e
835
+ @output_b = output_b
836
+ # result of last operation
837
+ @adjusted_digits = @digits = nil
838
+ # for "all-digits" mode results (which are truncated, rather than rounded),
839
+ # round_up contains information to round the result:
840
+ # * it is nil if the rest of digits are zero (the result is exact)
841
+ # * it is :lo if there exist non-zero digits beyond the significant ones (those returned), but
842
+ # the value is below the tie (the value must be rounded up only for :up rounding mode)
843
+ # * it is :tie if there exists exactly one nonzero digit after the significant and it is radix/2,
844
+ # for round-to-nearest it is atie.
845
+ # * it is :hi otherwise (the value should be rounded-up except for the :down mode)
846
+ @round_up = nil
847
+ end
848
+
849
+ # This method converts v = f*b**e into a sequence of output_b-base digits,
850
+ # so that if the digits are converted back to a floating-point value
851
+ # of precision p (correctly rounded), the result is v.
852
+ # If round_mode is not nil, just enough digits to produce v using
853
+ # that rounding is used; otherwise enough digits to produce v with
854
+ # any rounding are delivered.
855
+ #
856
+ # If the +all+ parameter is true, all significant digits are generated without rounding,
857
+ # i.e. all digits that, if used on input, cannot arbitrarily change
858
+ # while preserving the parsed value of the floating point number. Since the digits are not rounded
859
+ # more digits may be needed to assure round-trip value preservation.
860
+ # This is useful to reflect the precision of the floating point value in the output; in particular
861
+ # trailing significant zeros are shown. But note that, for directed rounding and base conversion
862
+ # this may need to produce an infinite number of digits, in which case an exception will be raised.
863
+ # This is specially frequent for the :up rounding mode, in which any number with a finite number
864
+ # of nonzero digits equal to or less than the precision will haver and infinite sequence of zero
865
+ # significant digits.
866
+ #
867
+ # With :down rounding (truncation) this could be used to show the exact value of the floating
868
+ # point but beware: when used with directed rounding, if the value has not an exact representation
869
+ # in the output base this will lead to an infinite loop.
870
+ # formatting '0.1' (as a decimal floating-point number) in base 2 with :down rounding
871
+ #
872
+ # When the +all+ parameters is used the result is not rounded (is truncated), and the round_up flag
873
+ # is set to indicate that nonzero digits exists beyond the returned digits; the possible values
874
+ # of the round_up flag are:
875
+ # * nil : the rest of digits are zero (the result is exact)
876
+ # * :lo : there exist non-zero digits beyond the significant ones (those returned), but
877
+ # the value is below the tie (the value must be rounded up only for :up rounding mode)
878
+ # * :tie : there exists exactly one nonzero digit after the significant and it is radix/2,
879
+ # for round-to-nearest it is atie.
880
+ # * :hi : the value is closer to the rounded-up value (incrementing the last significative digit.)
881
+ #
882
+ # Note that the round_mode here is not the rounding mode applied to the output;
883
+ # it is the rounding mode that applied to *input* preserves the original floating-point
884
+ # value (with the same precision as input).
885
+ # should be rounded-up.
886
+ def format(v, f, e, round_mode, p=nil, all=false)
887
+ context = v.class.context
888
+ # TODO: consider removing parameters f,e and using v.split instead
889
+ @minus = (context.sign(v)==-1)
890
+ @v = context.copy_sign(v, +1) # don't use context.abs(v) because it rounds (and may overflow also)
891
+ @f = f.abs
892
+ @e = e
893
+ @round_mode = round_mode
894
+ @all_digits = all
895
+ p ||= context.precision
896
+
897
+ # adjust the rounding mode to work only with positive numbers
898
+ @round_mode = Support.simplified_round_mode(@round_mode, @minus)
899
+
900
+ # determine the high,low inclusion flags of the rounding limits
901
+ case @round_mode
902
+ when :half_even
903
+ # rounding rage is (v-m-,v+m+) if v is odd and [v+m-,v+m+] if even
904
+ @round_l = @round_h = ((@f%2)==0)
905
+ when :up
906
+ # rounding rage is (v-,v]
907
+ # ceiling is treated here assuming f>0
908
+ @round_l, @round_h = false, true
909
+ when :down
910
+ # rounding rage is [v,v+)
911
+ # floor is treated here assuming f>0
912
+ @round_l, @round_h = true, false
913
+ when :half_up
914
+ # rounding rage is [v+m-,v+m+)
915
+ @round_l, @round_h = true, false
916
+ when :half_down
917
+ # rounding rage is (v+m-,v+m+]
918
+ @round_l, @round_h = false, true
919
+ else # :nearest
920
+ # Here assume only that round-to-nearest will be used, but not which variant of it
921
+ # The result is valid for any rounding (to nearest) but may produce more digits
922
+ # than stricly necessary for specific rounding modes.
923
+ # That is, enough digits are generated so that when the result is
924
+ # converted to floating point with the specified precision and
925
+ # correct rounding (to nearest), the result is the original number.
926
+ # rounding range is (v+m-,v+m+)
927
+ @round_l = @round_h = false
928
+ end
929
+
930
+ # TODO: use context.next_minus, next_plus instead of direct computing, don't require min_e & ps
931
+ # Now compute the working quotients @r/@s, @m_p/@s = (v+ - @v), @m_m/@s = (@v - v-) and scale them.
932
+ if @e >= 0
933
+ if @f != b_power(p-1)
934
+ be = b_power(@e)
935
+ @r, @s, @m_p, @m_m = @f*be*2, 2, be, be
936
+ else
937
+ be = b_power(@e)
938
+ be1 = be*@b
939
+ @r, @s, @m_p, @m_m = @f*be1*2, @b*2, be1, be
940
+ end
941
+ else
942
+ if @e==@min_e or @f != b_power(p-1)
943
+ @r, @s, @m_p, @m_m = @f*2, b_power(-@e)*2, 1, 1
944
+ else
945
+ @r, @s, @m_p, @m_m = @f*@b*2, b_power(1-@e)*2, @b, 1
946
+ end
947
+ end
948
+ @k = 0
949
+ @context = context
950
+ scale_optimized!
951
+
952
+
953
+ # The value to be formatted is @v=@r/@s; m- = @m_m/@s = (@v - v-)/@s; m+ = @m_p/@s = (v+ - @v)/@s
954
+ # Now adjust @m_m, @m_p so that they define the rounding range
955
+ case @round_mode
956
+ when :up
957
+ # ceiling is treated here assuming @f>0
958
+ # rounding range is -v,@v
959
+ @m_m, @m_p = @m_m*2, 0
960
+ when :down
961
+ # floor is treated here assuming #f>0
962
+ # rounding range is @v,v+
963
+ @m_m, @m_p = 0, @m_p*2
964
+ else
965
+ # rounding range is v-,v+
966
+ # @m_m, @m_p = @m_m, @m_p
967
+ end
968
+
969
+ # Now m_m, m_p define the rounding range
970
+ all ? generate_max : generate
971
+
972
+ end
973
+
974
+ # Access result of format operation: scaling (position of radix point) and digits
975
+ def digits
976
+ return @k, @digits
977
+ end
978
+
979
+ attr_reader :round_up
980
+
981
+
982
+ # Access rounded result of format operation: scaling (position of radix point) and digits
983
+ def adjusted_digits(round_mode)
984
+ round_mode = Support.simplified_round_mode(round_mode, @minus)
985
+ if @adjusted_digits.nil? && !@digits.nil?
986
+ increment = (@round_up && (round_mode != :down)) &&
987
+ ((round_mode == :up) ||
988
+ (@round_up == :hi) ||
989
+ ((@round_up == :tie) &&
990
+ ((round_mode==:half_up) || ((round_mode==:half_even) && ((@digits.last % 2)==1)))))
991
+ # increment = (@round_up == :tie) || (@round_up == :hi) # old behaviour (:half_up)
992
+ if increment
993
+ base = @output_b
994
+ dec_pos = @k
995
+ digits = @digits.dup
996
+ # carry = increment ? 1 : 0
997
+ # digits = digits.reverse.map{|d| d += carry; d>=base ? 0 : (carry=0;d)}.reverse
998
+ # if carry != 0
999
+ # digits.unshift carry
1000
+ # dec_pos += 1
1001
+ # end
1002
+ i = digits.size - 1
1003
+ while i>=0
1004
+ digits[i] += 1
1005
+ if digits[i] == base
1006
+ digits[i] = 0
1007
+ else
1008
+ break
1009
+ end
1010
+ i -= 1
1011
+ end
1012
+ if i<0
1013
+ dec_pos += 1
1014
+ digits.unshift 1
1015
+ end
1016
+ @adjusted_k = dec_pos
1017
+ @adjusted_digits = digits
1018
+ else
1019
+ @adjusted_k = @k
1020
+ @adjusted_digits = @digits
1021
+ end
1022
+ end
1023
+ return @adjusted_k, @adjusted_digits
1024
+ end
1025
+
1026
+ # Given r/s = v (number to convert to text), m_m/s = (v - v-)/s, m_p/s = (v+ - v)/s
1027
+ # Scale the fractions so that the first significant digit is right after the radix point, i.e.
1028
+ # find k = ceil(logB((r+m_p)/s)), the smallest integer such that (r+m_p)/s <= B^k
1029
+ # if k>=0 return:
1030
+ # r=r, s=s*B^k, m_p=m_p, m_m=m_m
1031
+ # if k<0 return:
1032
+ # r=r*B^k, s=s, m_p=m_p*B^k, m_m=m_m*B^k
1033
+ #
1034
+ # scale! is a general iterative method using only (multiprecision) integer arithmetic.
1035
+ def scale_original!(really=false)
1036
+ loop do
1037
+ if (@round_h ? (@r+@m_p >= @s) : (@r+@m_p > @s)) # k is too low
1038
+ @s *= @output_b
1039
+ @k += 1
1040
+ elsif (@round_h ? ((@r+@m_p)*@output_b<@s) : ((@r+@m_p)*@output_b<=@s)) # k is too high
1041
+ @r *= @output_b
1042
+ @m_p *= @output_b
1043
+ @m_m *= @output_b
1044
+ @k -= 1
1045
+ else
1046
+ break
1047
+ end
1048
+ end
1049
+ end
1050
+ # using local vars instead of instance vars: it makes a difference in performance
1051
+ def scale!
1052
+ r, s, m_p, m_m, k,output_b = @r, @s, @m_p, @m_m, @k,@output_b
1053
+ loop do
1054
+ if (@round_h ? (r+m_p >= s) : (r+m_p > s)) # k is too low
1055
+ s *= output_b
1056
+ k += 1
1057
+ elsif (@round_h ? ((r+m_p)*output_b<s) : ((r+m_p)*output_b<=s)) # k is too high
1058
+ r *= output_b
1059
+ m_p *= output_b
1060
+ m_m *= output_b
1061
+ k -= 1
1062
+ else
1063
+ @s = s
1064
+ @r = r
1065
+ @m_p = m_p
1066
+ @m_m = m_m
1067
+ @k = k
1068
+ break
1069
+ end
1070
+ end
1071
+ end
1072
+
1073
+ def b_power(n)
1074
+ @b**n
1075
+ end
1076
+
1077
+ def output_b_power(n)
1078
+ @output_b**n
1079
+ end
1080
+
1081
+ def generate_max
1082
+ @round_up = false
1083
+ list = []
1084
+ r, s, m_p, m_m, = @r, @s, @m_p, @m_m
1085
+ n_iters, rs = 0, []
1086
+ loop do
1087
+ if (n_iters > 10000)
1088
+ raise "Infinite digit sequence." if rs.include?(r)
1089
+ rs << r
1090
+ else
1091
+ n_iters += 1
1092
+ end
1093
+
1094
+ d,r = (r*@output_b).divmod(s)
1095
+
1096
+ m_p *= @output_b
1097
+ m_m *= @output_b
1098
+
1099
+ list << d
1100
+
1101
+ tc1 = @round_l ? (r<=m_m) : (r<m_m)
1102
+ tc2 = @round_h ? (r+m_p >= s) : (r+m_p > s)
1103
+
1104
+ if tc1 && tc2
1105
+ if r != 0
1106
+ r *= 2
1107
+ if r > s
1108
+ @round_up = :hi
1109
+ elsif r == s
1110
+ @round_up = :tie
1111
+ else
1112
+ @rund_up = :lo
1113
+ end
1114
+ end
1115
+ break
1116
+ end
1117
+ end
1118
+ @digits = list
1119
+ end
1120
+
1121
+ def generate
1122
+ list = []
1123
+ r, s, m_p, m_m, = @r, @s, @m_p, @m_m
1124
+ loop do
1125
+ d,r = (r*@output_b).divmod(s)
1126
+ m_p *= @output_b
1127
+ m_m *= @output_b
1128
+ tc1 = @round_l ? (r<=m_m) : (r<m_m)
1129
+ tc2 = @round_h ? (r+m_p >= s) : (r+m_p > s)
1130
+
1131
+ if not tc1
1132
+ if not tc2
1133
+ list << d
1134
+ else
1135
+ list << d+1
1136
+ break
1137
+ end
1138
+ else
1139
+ if not tc2
1140
+ list << d
1141
+ break
1142
+ else
1143
+ if r*2 < s
1144
+ list << d
1145
+ break
1146
+ else
1147
+ list << d+1
1148
+ break
1149
+ end
1150
+ end
1151
+ end
1152
+
1153
+ end
1154
+ @digits = list
1155
+ end
1156
+
1157
+ ESTIMATE_FLOAT_LOG_B = {2=>1/Math.log(2), 10=>1/Math.log(10), 16=>1/Math.log(16)}
1158
+ # scale_o1! is an optimized version of scale!; it requires an additional parameters with the
1159
+ # floating-point number v=r/s
1160
+ #
1161
+ # It uses a Float estimate of ceil(logB(v)) that may need to adjusted one unit up
1162
+ # TODO: find easy to use estimate; determine max distance to correct value and use it for fixing,
1163
+ # or use the general scale! for fixing (but remembar to multiply by exptt(...))
1164
+ # (determine when Math.log is aplicable, etc.)
1165
+ def scale_optimized!
1166
+ context = @context # @v.class.context
1167
+ return scale! if context.zero?(@v)
1168
+
1169
+ # 1. compute estimated_scale
1170
+
1171
+ # 1.1. try to use Float logarithms (Math.log)
1172
+ v = @v
1173
+ v_abs = context.copy_sign(v, +1) # don't use v.abs because it rounds (and may overflow also)
1174
+ v_flt = v_abs.to_f
1175
+ b = @output_b
1176
+ log_b = ESTIMATE_FLOAT_LOG_B[b]
1177
+ log_b = ESTIMATE_FLOAT_LOG_B[b] = 1.0/Math.log(b) if log_b.nil?
1178
+ estimated_scale = nil
1179
+ fixup = false
1180
+ begin
1181
+ l = ((b==10) ? Math.log10(v_flt) : Math.log(v_flt)*log_b)
1182
+ estimated_scale =(l - 1E-10).ceil
1183
+ fixup = true
1184
+ rescue
1185
+ # rescuing errors is more efficient than checking (v_abs < Float::MAX.to_i) && (v_flt > Float::MIN) when v is a Flt
1186
+ else
1187
+ # estimated_scale = nil
1188
+ end
1189
+
1190
+ # 1.2. Use Flt::DecNum logarithm
1191
+ if estimated_scale.nil?
1192
+ v.to_decimal_exact(:precision=>12) if v.is_a?(BinNum)
1193
+ if v.is_a?(DecNum)
1194
+ l = nil
1195
+ DecNum.context(:precision=>12) do
1196
+ case b
1197
+ when 10
1198
+ l = v_abs.log10
1199
+ else
1200
+ l = v_abs.ln/Flt.DecNum(b).ln
1201
+ end
1202
+ end
1203
+ l -= Flt.DecNum(+1,1,-10)
1204
+ estimated_scale = l.ceil
1205
+ fixup = true
1206
+ end
1207
+ end
1208
+
1209
+ # 1.3 more rough Float aproximation
1210
+ # TODO: optimize denominator, correct numerator for more precision with first digit or part
1211
+ # of the coefficient (like _log_10_lb)
1212
+ estimated_scale ||= (v.adjusted_exponent.to_f * Math.log(v.class.context.radix) * log_b).ceil
1213
+
1214
+ if estimated_scale >= 0
1215
+ @k = estimated_scale
1216
+ @s *= output_b_power(estimated_scale)
1217
+ else
1218
+ sc = output_b_power(-estimated_scale)
1219
+ @k = estimated_scale
1220
+ @r *= sc
1221
+ @m_p *= sc
1222
+ @m_m *= sc
1223
+ end
1224
+ fixup ? scale_fixup! : scale!
1225
+
1226
+ end
1227
+
1228
+ # fix up scaling (final step): specialized version of scale!
1229
+ # This performs a single up scaling step, i.e. behaves like scale2, but
1230
+ # the input must be at most one step down from the final result
1231
+ def scale_fixup!
1232
+ if (@round_h ? (@r+@m_p >= @s) : (@r+@m_p > @s)) # too low?
1233
+ @s *= @output_b
1234
+ @k += 1
1235
+ end
1236
+ end
1237
+
1238
+ end
1239
+
1240
+ module AuxiliarFunctions
1241
+
1242
+ module_function
1243
+
1244
+ # Number of bits in binary representation of the positive integer n, or 0 if n == 0.
1245
+ def _nbits(x)
1246
+ raise TypeError, "The argument to _nbits should be nonnegative." if x < 0
1247
+ if x.is_a?(Fixnum)
1248
+ return 0 if x==0
1249
+ x.to_s(2).length
1250
+ elsif x <= NBITS_LIMIT
1251
+ Math.frexp(x).last
1252
+ else
1253
+ n = 0
1254
+ while x!=0
1255
+ y = x
1256
+ x >>= NBITS_BLOCK
1257
+ n += NBITS_BLOCK
1258
+ end
1259
+ n += y.to_s(2).length - NBITS_BLOCK if y!=0
1260
+ n
1261
+ end
1262
+ end
1263
+ NBITS_BLOCK = 32
1264
+ NBITS_LIMIT = Math.ldexp(1,Float::MANT_DIG).to_i
1265
+
1266
+ # Number of base b digits in an integer
1267
+ def _ndigits(x, b)
1268
+ raise TypeError, "The argument to _ndigits should be nonnegative." if x < 0
1269
+ return 0 unless x.is_a?(Integer)
1270
+ return _nbits(x) if b==2
1271
+ if x.is_a?(Fixnum)
1272
+ return 0 if x==0
1273
+ x.to_s(b).length
1274
+ elsif x <= NDIGITS_LIMIT
1275
+ (Math.log(x)/Math.log(b)).floor + 1
1276
+ else
1277
+ n = 0
1278
+ block = b**NDIGITS_BLOCK
1279
+ while x!=0
1280
+ y = x
1281
+ x /= block
1282
+ n += NDIGITS_BLOCK
1283
+ end
1284
+ n += y.to_s(b).length - NDIGITS_BLOCK if y!=0
1285
+ n
1286
+ end
1287
+ end
1288
+ NDIGITS_BLOCK = 50
1289
+ NDIGITS_LIMIT = Float::MAX.to_i
1290
+
1291
+ def detect_float_rounding
1292
+ x = x = Math::ldexp(1, Float::MANT_DIG+1) # 10000...00*Float::RADIX**2 == Float::RADIX**(Float::MANT_DIG+1)
1293
+ y = x + Math::ldexp(1, 2) # 00000...01*Float::RADIX**2 == Float::RADIX**2
1294
+ h = Float::RADIX/2
1295
+ b = h*Float::RADIX
1296
+ z = Float::RADIX**2 - 1
1297
+ if x + 1 == y
1298
+ if (y + 1 == y) && Float::RADIX==10
1299
+ :up05
1300
+ elsif -x - 1 == -y
1301
+ :up
1302
+ else
1303
+ :ceiling
1304
+ end
1305
+ else # x + 1 == x
1306
+ if x + z == x
1307
+ if -x - z == -x
1308
+ :down
1309
+ else
1310
+ :floor
1311
+ end
1312
+ else # x + z == y
1313
+ # round to nearest
1314
+ if x + b == x
1315
+ if y + b == y
1316
+ :half_down
1317
+ else
1318
+ :half_even
1319
+ end
1320
+ else # x + b == y
1321
+ :half_up
1322
+ end
1323
+ end
1324
+ end
1325
+
1326
+ end # Formatter
1327
+
1328
+ end # AuxiliarFunctions
1329
+
1330
+ end # Support
1331
+
1332
+
1333
+
1334
+
1335
+ end # Flt