flt 1.3.4 → 1.4.0

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