flt 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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