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