nio 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,1883 +1,1917 @@
1
- # Formatting numbers as text
2
-
3
- # Copyright (C) 2003-2005, Javier Goizueta <javier@goizueta.info>
4
- #
5
- # This program is free software; you can redistribute it and/or
6
- # modify it under the terms of the GNU General Public License
7
- # as published by the Free Software Foundation; either version 2
8
- # of the License, or (at your option) any later version.
9
-
10
-
11
- require 'nio/tools'
12
-
13
- require 'nio/repdec'
14
-
15
- require 'nio/rtnlzr'
16
-
17
- require 'rational'
18
-
19
- require 'bigdecimal'
20
-
21
- module Nio
22
-
23
- # positional notation, unformatted numeric literal: used as intermediate form
24
- class NeutralNum
25
- include StateEquivalent
26
- def initialize(s='',d='',p=nil,r=nil,dgs=DigitsDef.base(10), inexact=false, round=:inf)
27
- set s,d,p,r,dgs,dgs, inexact, round
28
- end
29
- attr_reader :sign, :digits, :dec_pos, :rep_pos, :special, :inexact, :rounding
30
- attr_writer :sign, :digits, :dec_pos, :rep_pos, :special, :inexact, :rounding
31
-
32
- # set number
33
- def set(s,d,p=nil,r=nil,dgs=DigitsDef.base(10),inexact=false,rounding=:inf,normalize=true)
34
- @sign = s # sign: '+','-',''
35
- @digits = d # digits string
36
- @dec_pos = p==nil ? d.length : p # position of decimal point: 0=before first digit...
37
- @rep_pos = r==nil ? d.length : r # first repeated digit (0=first digit...)
38
- @dgs = dgs
39
- @base = @dgs.radix
40
- @inexact = inexact
41
- @special = nil
42
- @rounding = rounding
43
- trimZeros unless inexact
44
- self
45
- end
46
- # set infinite (:inf) and invalid (:nan) numbers
47
- def set_special(s,sgn='') # :inf, :nan
48
- @special = s
49
- @sign = sgn
50
- self
51
- end
52
-
53
- def base
54
- @base
55
- end
56
- def base_digits
57
- @dgs
58
- end
59
- def base_digits=(dd)
60
- @dgs = dd
61
- @base = @dgs.radix
62
- end
63
- def base=(b)
64
- @dgs = DigitsDef.base(b)
65
- @base=@dgs.radix
66
- end
67
-
68
- # check for special numbers (which have only special and sign attributes)
69
- def special?
70
- special != nil
71
- end
72
-
73
- # check for special numbers (which have only special and sign attributes)
74
- def inexact?
75
- @inexact
76
- end
77
-
78
- def dup
79
- n = NeutralNum.new
80
- if special?
81
- n.set_special @special.dup, @sign.dup
82
- else
83
- #n.set @sign.dup, @digits.dup, @dec_pos.dup, @rep_pos.dup, @dgs.dup
84
- # in Ruby 1.6.8 Float,BigNum,Fixnum doesn't respond to dup
85
- n.set @sign.dup, @digits.dup, @dec_pos, @rep_pos, @dgs.dup, @inexact, @rounding
86
- end
87
- return n
88
- end
89
-
90
- def zero?
91
- z = false
92
- if !special
93
- if digits==''
94
- z = true
95
- else
96
- z = true
97
- for i in (0...@digits.length)
98
- if dig_value(i)!=0
99
- z = false
100
- break
101
- end
102
- end
103
- end
104
- end
105
- z
106
- end
107
-
108
- def round!(n, mode=:fix, dir=nil)
109
- dir ||= rounding
110
- trimLeadZeros
111
- if n==:exact
112
- return unless @inexact
113
- n = @digits.size
114
- end
115
-
116
- n += @dec_pos if mode==:fix
117
- n = [n,@digits.size].min if @inexact
118
-
119
- adj = 0
120
- dv = :tie
121
- if @inexact && n==@digits.size
122
- dv = @inexact==:roundup ? :hi : :lo
123
- else
124
- v = dig_value(n)
125
- v2 = 2*v
126
- if v2 < @base # v<((@base+1)/2)
127
- dv = :lo
128
- elsif v2 > @base # v>(@base/2)
129
- dv = :hi
130
- else
131
-
132
- (n+1...@digits.length).each do |i|
133
- if dig_value(i)>0
134
- dv = :hi
135
- break
136
- end
137
- end
138
-
139
- dv = :hi if dv==:tie && @rep_pos<=n
140
- end
141
- end
142
-
143
- if dv==:hi
144
- adj = +1
145
- elsif dv==:tie
146
- if dir==:inf # towards nearest +/-infinity
147
- adj = +1
148
- elsif dir==:even # to nearest even digit (IEEE unbiased rounding)
149
- adj = +1 if (dig_value(n-1)%2)!=0
150
- elsif dir==:zero # towards zero
151
- adj=0
152
- # elsif dir==:odd
153
- # adj = +1 unless (dig_value(n-1)%2)!=0
154
- end
155
- end
156
-
157
- if n>@digits.length
158
- (@digits.length...n).each do |i|
159
- @digits << dig_char(dig_value(i))
160
- @rep_pos += 1
161
- end
162
- end
163
-
164
- prefix = ''
165
- i = n-1
166
- while adj!=0
167
- v = dig_value(i)
168
- v += adj
169
- adj = 0
170
- if v<0
171
- v += @base
172
- adj = -1
173
- elsif v>=@base
174
- v -= @base
175
- adj = +1
176
- end
177
- if i<0
178
- prefix = dig_char(v)+prefix
179
- elsif i<@digits.length
180
- @digits[i] = dig_char(v)
181
- end
182
- i += -1
183
- end
184
-
185
- if n<0
186
- @digits = ""
187
- else
188
- @digits = @digits[0...n]
189
- end
190
- @rep_pos = @digits.length
191
-
192
- if prefix!=''
193
- @digits = prefix + @digits
194
- @dec_pos += prefix.length
195
- @rep_pos += prefix.length
196
- end
197
-
198
-
199
- end
200
-
201
- def round(n, mode=:fix, dir=nil)
202
- dir ||= rounding
203
- nn = dup
204
- nn.round!(n,mode,dir)
205
- return nn
206
- end
207
-
208
- def trimTrailZeros()
209
- i = @digits.length
210
- while i>0 && dig_value(i-1)==0
211
- i -= 1
212
- end
213
- if @rep_pos>=i
214
- @digits = @digits[0...i]
215
- @rep_pos = i
216
- end
217
-
218
- if @digits==''
219
- @digits = dig_char(0) # '0'
220
- @rep_pos = 1
221
- @dec_pos = 1
222
- end
223
-
224
- end
225
-
226
- def trimLeadZeros()
227
- i = 0
228
- while i<@digits.length && dig_value(i)==0
229
- i += 1
230
- end
231
- @digits = @digits[i...@digits.length]
232
- @dec_pos -= i
233
- @rep_pos -= i
234
-
235
- if @digits==''
236
- @digits = dig_char(0) # '0'
237
- @rep_pos = 1
238
- @dec_pos = 1
239
- end
240
-
241
- end
242
-
243
- def trimZeros()
244
- trimLeadZeros
245
- trimTrailZeros
246
- end
247
-
248
- protected
249
-
250
- def dig_value(i)
251
- v = 0
252
- if i>=@rep_pos
253
- i -= @digits.length
254
- i %= @digits.length - @rep_pos if @rep_pos<@digits.length
255
- i += @rep_pos
256
- end
257
- if i>=0 && i<@digits.length
258
- v = @dgs.digit_value(@digits[i]) #digcode_value(@digits[i])
259
- end
260
- return v>=0 && v<@base ? v : nil
261
- end
262
- #def digcode_value(c)
263
- # v = c-?0
264
- # if v>9
265
- # v = 10 + c.chr.downcase[0] - ?a
266
- # end
267
- # v
268
- # @dgs.digit_value(c)
269
- #end
270
-
271
- def dig_char(v)
272
- c = ''
273
- if v!=nil && v>=0 && v<@base
274
- c = @dgs.digit_char(v).chr
275
- end
276
- c
277
- end
278
-
279
- end
280
-
281
- class NeutralNum
282
- public
283
- def to_RepDec
284
- n = RepDec.new(@base)
285
- if special?
286
-
287
- case special
288
- when :nan
289
- n.ip = :indeterminate
290
- when :inf
291
- if sign=='-'
292
- n.ip = :posinfinity
293
- else
294
- n.ip :neginfinity
295
- end
296
- else
297
- n = nil
298
- end
299
-
300
- else
301
- if dec_pos<=0
302
- n.ip = 0
303
- n.d = text_to_digits(dig_char(0)*(-dec_pos) + digits)
304
- elsif dec_pos >= digits.length
305
- n.ip = digits.to_i(@base)
306
- if rep_pos<dec_pos
307
- i=0
308
- (dec_pos-digits.length).times do
309
- n.ip *= @base
310
- n.ip += @dgs.digit_value(digits[rep_pos+i]) if rep_pos+i<digits.length
311
- i += 1
312
- i=0 if i>=digits.length-rep_pos
313
- end
314
- n.d = []
315
- while i<digits.length-rep_pos
316
- n.d << @dgs.digit_value(digits[rep_pos+i])
317
- i += 1
318
- end
319
- new_rep_pos = n.d.size + dec_pos
320
- n.d += text_to_digits(digits[rep_pos..-1])
321
- self.rep_pos = new_rep_pos
322
- else
323
- n.ip *= @base**(dec_pos-digits.length)
324
- n.d = []
325
- end
326
- else
327
- n.ip = digits[0...dec_pos].to_i(@base)
328
- n.d = text_to_digits(digits[dec_pos..-1])
329
- if rep_pos<dec_pos
330
- new_rep_pos = n.d.size + dec_pos
331
- n.d += text_to_digits(digits[rep_pos..-1])
332
- self.rep_pos = new_rep_pos
333
- puts "--rep_pos=#{rep_pos}"
334
- end
335
- end
336
- n.sign = -1 if sign=='-'
337
- n.rep_i = rep_pos - dec_pos
338
- end
339
- n.normalize!(!inexact) # keep trailing zeros for inexact numbers
340
- return n
341
- end
342
- protected
343
- def text_to_digits(txt)
344
- #txt.split('').collect{|c| @dgs.digit_value(c)}
345
- ds = []
346
- txt.each_byte{|b| ds << @dgs.digit_value(b)}
347
- ds
348
- end
349
- end
350
-
351
- class RepDec
352
- public
353
- def to_NeutralNum(base_dgs=nil)
354
- num = NeutralNum.new
355
- if !ip.is_a?(Integer)
356
-
357
- case ip
358
- when :indeterminate
359
- num.set_special :nan
360
- when :posinfinity
361
- num.set_special :inf,'+'
362
- when :neginfinity
363
- num.set_special :inf,'-'
364
- else
365
- num = nil
366
- end
367
-
368
- else
369
- base_dgs ||= DigitsDef.base(@radix)
370
- # assert base_dgs.radix == @radix
371
- signch = sign<0 ? '-' : '+'
372
- decimals = ip.to_s(@radix)
373
- dec_pos = decimals.length
374
- d.each {|dig| decimals << base_dgs.digit_char(dig) }
375
- rep_pos = rep_i==nil ? decimals.length : dec_pos + rep_i
376
- num.set signch, decimals, dec_pos, rep_pos, base_dgs
377
- end
378
- return num
379
- end
380
- end
381
-
382
- # A Fmt object defines a numeric format.
383
- #
384
- # The formatting aspects managed by Fmt are:
385
- # * mode and precision
386
- # - #mode() and #orec() set the main paramters
387
- # - see also #show_all_digits(), #approx_mode(), #insignificant_digits(),
388
- # #sci_digits() and #show_plus()
389
- # * separators
390
- # - see #sep() and #grouping()
391
- # * field justfification
392
- # - #width() and the shortcut #pad0s()
393
- # * numerical base
394
- # - #base()
395
- # * repeating numerals
396
- # - #rep()
397
- #
398
- # Note that for every aspect there are also corresponding _mutator_
399
- # methos (its name ending with a bang) that modify an object in place,
400
- # instead of returning an altered copy.
401
- #
402
- # This class also contains class methods for numeric conversion:
403
- # * Fmt.convert
404
- # and for default and other predefined formats:
405
- # * Fmt.default / Fmt.default=
406
- # * Fmt.[] / Fmt.[]=
407
- #
408
- # The actual formatted reading and writting if performed by
409
- # * #nio_write() (Nio::Formattable#nio_write)
410
- # * #nio_read() (Nio::Formattable::ClassMethods#nio_read)
411
- # Finally numerical objects can be rounded according to a format:
412
- # * #nio_round() (Nio::Formattable#nio_round)
413
- class Fmt
414
- include StateEquivalent
415
-
416
- class Error < StandardError # :nodoc:
417
- end
418
- class InvalidOption < Error # :nodoc:
419
- end
420
- class InvalidFormat < Error # :nodoc:
421
- end
422
-
423
- @@default_rounding_mode = :even
424
- def initialize()
425
-
426
- @dec_sep = '.'
427
- @grp_sep = ','
428
- @grp = []
429
-
430
- @ndig = :exact
431
- @mode=:gen
432
- @round=Fmt.default_rounding_mode
433
- @all_digits = false
434
- @approx = :only_sig
435
- @non_sig = '' # marker for insignificant digits of inexact values e.g. '#','0'
436
- @sci_format = 1 # number of integral digits in the mantissa: -1 for all
437
-
438
- @show_plus = false
439
-
440
- @rep_begin = '<'
441
- @rep_end = '>'
442
- @rep_auto = '...'
443
- @rep_n = 2
444
- @rep_in = true
445
-
446
- @width = 0
447
- @fill_char = ' '
448
- @adjust=:right
449
-
450
- @base_radix = 10
451
- @base_uppercase = true
452
- @base_digits = DigitsDef.base(@base_radix, !@base_uppercase)
453
- @show_base = false
454
- @base_indicators = { 2=>'b', 8=>'o', 10=>'', 16=>'h', 0=>'r'} # 0: generic (used with radix)
455
- @base_prefix = false
456
-
457
- @nan_txt = 'NAN'
458
- @inf_txt = 'Infinity'
459
-
460
- yield self if block_given?
461
- end
462
-
463
- # Defines the separators used in numerals. This is relevant to
464
- # both input and output.
465
- #
466
- # The first argument is the radix point separator (usually
467
- # a point or a comma; by default it is a point.)
468
- #
469
- # The second argument is the group separator.
470
- #
471
- # Finally, the third argument is an array that defines the groups
472
- # of digits to separate.
473
- # By default it's [], which means that no grouping will be produced on output
474
- # (but the group separator defined will be ignored in input.)
475
- # To produce the common thousands separation a value of [3] must be passed,
476
- # which means that groups of 3 digits are used.
477
- def sep(dec_sep,grp_sep=nil,grp=nil)
478
- dup.sep!(dec_sep,grp_sep,grp)
479
- end
480
- # This is the mutator version of #sep().
481
- def sep!(dec_sep,grp_sep=nil,grp=nil)
482
- set! :dec_sep=>dec_sep, :grp_sep=>grp_sep, :grp=>grp
483
- end
484
-
485
- # This defines the grouping of digits (which can also be defined in #sep()
486
- def grouping(grp=[3],grp_sep=nil)
487
- dup.grouping!(grp,grp_sep)
488
- end
489
- # This is the mutator version of #grouping().
490
- def grouping!(grp=[3],grp_sep=nil)
491
- set! :grp_sep=>grp_sep, :grp=>grp
492
- end
493
-
494
- # This is a shortcut to return a new default Fmt object
495
- # and define the separators as with #sep().
496
- def Fmt.sep(dec_sep,grp_sep=nil,grp=nil)
497
- Fmt.default.sep(dec_sep,grp_sep,grp)
498
- end
499
- # This is a shortcut to return a new default Fmt object
500
- # and define the grouping as with #grouping().
501
- def Fmt.grouping(grp=[3],grp_sep=nil)
502
- Fmt.default.grouping(grp,grp_sep)
503
- end
504
-
505
- # Define the formatting mode. There are two fixed parameters:
506
- # - <tt>mode</tt> (only relevant for output)
507
- # [<tt>:gen</tt>]
508
- # (general) chooses automatically the shortes format
509
- # [<tt>:fix</tt>]
510
- # (fixed precision) is a simple format with a fixed number of digits
511
- # after the point
512
- # [<tt>:sig</tt>]
513
- # (significance precision) is like :fix but using significant digits
514
- # [<tt>:sci</tt>]
515
- # (scientific) is the exponential form 1.234E2
516
- # - <tt>precision</tt> (number of digits or :exact, only used for output)
517
- # [<tt>exact</tt>]
518
- # means that as many digits as necessary to unambiguosly define the
519
- # value are used; this is the default.
520
- #
521
- # Other paramters can be passed in a hash after <tt>precision</tt>
522
- # - <tt>:round</tt> rounding mode applied to conversions
523
- # (this is relevant for both input and output). It must be one of:
524
- # [<tt>:inf</tt>]
525
- # rounds to nearest with ties toward infinite;
526
- # 1.5 is rounded to 2, -1.5 to -2
527
- # [<tt>:zero</tt>]
528
- # rounds to nearest with ties toward zero;
529
- # 1.5 is rounded to 1, -1.5 to 2
530
- # [<tt>:even</tt>]
531
- # rounds to the nearest with ties toward an even digit;
532
- # 1.5 rounds to 2, 2.5 to 2
533
- # - <tt>:approx</tt> approximate mode
534
- # [<tt>:only_sig</tt>]
535
- # (the default) treats the value as an approximation and only
536
- # significant digits (those that cannot take an arbitrary value without
537
- # changing the specified value) are shown.
538
- # [<tt>:exact</tt>]
539
- # the value is interpreted as exact, there's no distinction between
540
- # significant and insignificant digits.
541
- # [<tt>:simplify</tt>]
542
- # the value is simplified, if possible to a simpler (rational) value.
543
- # - <tt>:show_all_digits</tt> if true, this forces to show digits that
544
- # would otherwise not be shown in the <tt>:gen</tt> format: trailing
545
- # zeros of exact types or non-signficative digits of inexact types.
546
- # - <tt>:nonsignficative_digits</tt> assigns a character to display
547
- # insignificant digits, # by default
548
- def mode(mode,precision=nil,options={})
549
- dup.mode!(mode,precision,options)
550
- end
551
- # This is the mutator version of #mode().
552
- def mode!(mode,precision=nil,options={})
553
- set! options.merge(:mode=>mode, :ndig=>precision)
554
- end
555
-
556
- # Defines the formatting mode like #mode() but using a different
557
- # order of the first two parameters parameters, which is useful
558
- # to change the precision only. Refer to #mode().
559
- def prec(precision,mode=nil, options={})
560
- dup.prec! precision, mode, options
561
- end
562
- # This is the mutator version of #prec().
563
- def prec!(precision,mode=:gen, options={})
564
- set! options.merge(:mode=>mode, :ndig=>precision)
565
- end
566
-
567
- # This is a shortcut to return a new default Fmt object
568
- # and define the formatting mode as with #mode()
569
- def Fmt.mode(mode,ndig=nil,options={})
570
- Fmt.default.mode(mode,ndig,options)
571
- end
572
- # This is a shortcut to return a new default Fmt object
573
- # and define the formatting mode as with #prec()
574
- def Fmt.prec(ndig,mode=nil,options={})
575
- Fmt.default.prec(ndig,mode,options)
576
- end
577
-
578
- # Rounding mode used when not specified otherwise
579
- def Fmt.default_rounding_mode
580
- @@default_rounding_mode
581
- end
582
- # The default rounding can be changed here; it starts with the value :even.
583
- # See the rounding modes available in the description of method #mode().
584
- def Fmt.default_rounding_mode=(m)
585
- @@default_rounding_mode=m
586
- Fmt.default = Fmt.default.round(m)
587
- end
588
-
589
- # This controls the display of the digits that are not necessary
590
- # to specify the value unambiguosly (e.g. trailing zeros).
591
- #
592
- # The true (default) value forces the display of the requested number of digits
593
- # and false will display only necessary digits.
594
- def show_all_digits(ad=true)
595
- dup.show_all_digits! ad
596
- end
597
- # This is the mutator version of #show_all_digits().
598
- def show_all_digits!(ad=true)
599
- set! :all_digits=>ad
600
- end
601
- # This defines the approximate mode (:only_sig, :exact, :simplify)
602
- # just like the last parameter of #mode()
603
- def approx_mode(mode)
604
- dup.approx_mode! mode
605
- end
606
- # This is the mutator version of #approx_mode().
607
- def approx_mode!(mode)
608
- set! :approx=>mode
609
- end
610
- # Defines a character to stand for insignificant digits when
611
- # a specific number of digits has been requested greater than then
612
- # number of significant digits (for approximate types).
613
- def insignificant_digits(ch='#')
614
- dup.insignificant_digits! ch
615
- end
616
- # This is the mutator version of #insignificant_digits().
617
- def insignificant_digits!(ch='#')
618
- ch ||= ''
619
- set! :non_sig=>ch
620
- end
621
- # Defines the number of significan digits before the radix separator
622
- # in scientific notation. A negative value will set all significant digits
623
- # before the radix separator. The special value <tt>:eng</tt> activates
624
- # _engineering_ mode, in which the exponents are multiples of 3.
625
- #
626
- # For example:
627
- # 0.1234.nio_write(Fmt.mode(:sci,4).sci_digits(0) -> 0.1234E0
628
- # 0.1234.nio_write(Fmt.mode(:sci,4).sci_digits(3) -> 123.4E-3
629
- # 0.1234.nio_write(Fmt.mode(:sci,4).sci_digits(-1) -> 1234.E-4
630
- # 0.1234.nio_write(Fmt.mode(:sci,4).sci_digits(:eng) -> 123.4E-3
631
- def sci_digits(n=-1)
632
- dup.sci_digits! n
633
- end
634
- # This is the mutator version of #sci_digits().
635
- def sci_digits!(n=-1)
636
- set! :sci_format=>n
637
- end
638
-
639
- # This is a shortcut to return a new default Fmt object
640
- # and define show_all_digits
641
- def Fmt.show_all_digits(v=true)
642
- Fmt.default.show_all_digits(v)
643
- end
644
- # This is a shortcut to return a new default Fmt object
645
- # and define approx_mode
646
- def Fmt.approx_mode(v)
647
- Fmt.default.approx_mode(v)
648
- end
649
- # This is a shortcut to return a new default Fmt object
650
- # and define insignificant digits
651
- def Fmt.insignificant_digits(v='#')
652
- Fmt.default.insignificant_digits(v)
653
- end
654
- # This is a shortcut to return a new default Fmt object
655
- # and define sci_digits
656
- def Fmt.sci_digits(v=-1)
657
- Fmt.default.sci_digits(v)
658
- end
659
-
660
- # Controls the display of the sign for positive numbers
661
- def show_plus(sp=true)
662
- dup.show_plus! sp
663
- end
664
- # This is the mutator version of #show_plus().
665
- def show_plus!(sp=true)
666
- set! :show_plus=>sp
667
- end
668
-
669
- # This is a shortcut to return a new default Fmt object
670
- # and define show_plus
671
- def Fmt.show_plus(v=true)
672
- Fmt.default.show_plus(v)
673
- end
674
-
675
- # Defines the handling and notation for repeating numerals. The parameters
676
- # can be passed in order or in a hash:
677
- # [<tt>:begin</tt>] is the beginning delimiter of repeating section (<)
678
- # [<tt>:end</tt>] is the ending delimiter of repeating section (<)
679
- # [<tt>:suffix</tt>] is the suffix used to indicate a implicit repeating decimal
680
- # [<tt>:rep</tt>]
681
- # if this parameter is greater than zero, on output the repeating section
682
- # is repeated the indicated number of times followed by the suffix;
683
- # otherwise the delimited notation is used.
684
- # [<tt>:read</tt>]
685
- # (true/false) determines if repeating decimals are
686
- # recognized on input (true)
687
- def rep(*params)
688
- dup.rep!(*params)
689
- end
690
- # This is the mutator version of #rep().
691
- def rep!(*params)
692
-
693
- params << {} if params.size==0
694
- if params[0].kind_of?(Hash)
695
- params = params[0]
696
- else
697
- begch,endch,autoch,rep,read = *params
698
- params = {:begin=>begch,:end=>endch,:suffix=>autoch,:nreps=>rep,:read=>read}
699
- end
700
-
701
- set! params
702
- end
703
-
704
- # This is a shortcut to return a new default Fmt object
705
- # and define the repeating decimals mode as with #rep()
706
- def Fmt.rep(*params)
707
- Fmt.default.rep(*params)
708
- end
709
-
710
- # Sets the justificaton width, mode and fill character
711
- #
712
- # The mode accepts these values:
713
- # [<tt>:right</tt>] (the default) justifies to the right (adds padding at the left)
714
- # [<tt>:left</tt>] justifies to the left (adds padding to the right)
715
- # [<tt>:internal</tt>] like :right, but the sign is kept to the left, outside the padding.
716
- # [<tt>:center</tt>] centers the number in the field
717
- def width(w,adj=nil,ch=nil)
718
- dup.width! w,adj,ch
719
- end
720
- # This is the mutator version of #width().
721
- def width!(w,adj=nil,ch=nil)
722
- set! :width=>w, :adjust=>adj, :fill_char=>ch
723
- end
724
- # Defines the justification (as #width()) with the given
725
- # width, internal mode and filling with zeros.
726
- #
727
- # Note that if you also use grouping separators, the filling 0s
728
- # will not be separated.
729
- def pad0s(w)
730
- dup.pad0s! w
731
- end
732
- # This is the mutator version of #pad0s().
733
- def pad0s!(w)
734
- width! w, :internal, '0'
735
- end
736
- # This is a shortcut to create a new Fmt object and define the width
737
- # parameters as with #widht()
738
- def Fmt.width(w,adj=nil,ch=nil)
739
- Fmt.default.width(w,adj,ch)
740
- end
741
- # This is a shortcut to create a new Fmt object and define numeric
742
- # padding as with #pad0s()
743
- def Fmt.pad0s(w)
744
- Fmt.default.pad0s(w)
745
- end
746
-
747
- # defines the numerical base; the second parameters forces the use
748
- # of uppercase letters for bases greater than 10.
749
- def base(b, uppercase=nil)
750
- dup.base! b, uppercase
751
- end
752
- # This is the mutator version of #base().
753
- def base!(b, uppercase=nil)
754
- set! :base_radix=>b, :base_uppercase=>uppercase
755
- end
756
- # This is a shortcut to create a new Fmt object and define the base
757
- def Fmt.base(b, uppercase=nil)
758
- Fmt.default.base(b, uppercase)
759
- end
760
- # returns the exponent char used with the specified base
761
- def get_exp_char(base) # :nodoc:
762
- base ||= @base_radix
763
- base<=10 ? 'E' : '^'
764
- end
765
-
766
- # returns the base
767
- def get_base # :nodoc:
768
- @base_radix
769
- end
770
- # returns the digit characters used for a base
771
- def get_base_digits(b=nil) # :nodoc:
772
- (b.nil? || b==@base_radix) ? @base_digits : DigitsDef.base(b,!@base_uppercase)
773
- end
774
- # returns true if uppercase digits are used
775
- def get_base_uppercase? # :nodoc:
776
- @base_uppercase
777
- end
778
-
779
- # returns the formatting mode
780
- def get_mode # :nodoc:
781
- @mode
782
- end
783
- # returns the precision (number of digits)
784
- def get_ndig # :nodoc:
785
- @ndig
786
- end
787
- # return the show_all_digits state
788
- def get_all_digits? # :nodoc:
789
- @all_digits
790
- end
791
- # returns the approximate mode
792
- def get_approx # :nodoc:
793
- @approx
794
- end
795
-
796
- # returns the rounding mode
797
- def get_round # :nodoc:
798
- @round
799
- end
800
-
801
- # Method used internally to format a neutral numeral
802
- def nio_write_formatted(neutral) # :nodoc:
803
- str = ''
804
- if neutral.special?
805
- str << neutral.sign
806
- case neutral.special
807
- when :inf
808
- str << @inf_txt
809
- when :nan
810
- str << @nan_txt
811
- end
812
- else
813
- zero = get_base_digits(neutral.base).digit_char(0).chr
814
- neutral = neutral.dup
815
- round! neutral
816
- if neutral.zero?
817
- str << neutral.sign if neutral.sign=='-' # show - if number was <0 before rounding
818
- str << zero
819
- if @ndig.kind_of?(Numeric) && @ndig>0 && @mode==:fix
820
- str << @dec_sep << zero*@ndig
821
- end
822
- else
823
-
824
- neutral.trimLeadZeros
825
- actual_mode = @mode
826
- trim_trail_zeros = !@all_digits # false
827
-
828
- integral_digits = @sci_format
829
- if integral_digits == :eng
830
- integral_digits = 1
831
- while (neutral.dec_pos - integral_digits).modulo(3) != 0
832
- integral_digits += 1
833
- end
834
- elsif integral_digits==:all || integral_digits < 0
835
- if neutral.inexact? && @non_sig!='' && @ndig.kind_of?(Numeric)
836
- integral_digits = @ndig
837
- else
838
- integral_digits = neutral.digits.length
839
- end
840
- end
841
- exp = neutral.dec_pos - integral_digits
842
-
843
- case actual_mode
844
- when :gen # general (automatic)
845
- # @ndig means significant digits
846
- actual_mode = :sig
847
- actual_mode = :sci if use_scientific?(neutral, exp)
848
- trim_trail_zeros = !@all_digits # true
849
- end
850
-
851
- case actual_mode
852
- when :fix, :sig #, :gen
853
-
854
- str << neutral.sign if @show_plus || neutral.sign!='+'
855
-
856
- if @show_base && @base_prefix
857
- b_prefix = @base_indicators[neutral.base]
858
- str << b_prefix if b_prefix
859
- end
860
-
861
- if @ndig==:exact
862
- neutral.sign = '+'
863
- str << neutral.to_RepDec.getS(@rep_n, getRepDecOpt(neutral.base))
864
- else
865
- #zero = get_base_digits.digit_char(0).chr
866
- ns_digits = ''
867
-
868
- nd = neutral.digits.length
869
- if actual_mode==:fix
870
- nd -= neutral.dec_pos
871
- end
872
- if neutral.inexact? && @ndig>nd # assert no rep-dec.
873
- ns_digits = @non_sig*(@ndig-nd)
874
- end
875
-
876
- digits = neutral.digits + ns_digits
877
- if neutral.dec_pos<=0
878
- str << zero+@dec_sep+zero*(-neutral.dec_pos) + digits
879
- elsif neutral.dec_pos >= digits.length
880
- str << group(digits + zero*(neutral.dec_pos-digits.length))
881
- else
882
- str << group(digits[0...neutral.dec_pos]) + @dec_sep + digits[neutral.dec_pos..-1]
883
- end
884
- end
885
-
886
- #str = str.chomp(zero).chomp(@dec_sep) if trim_trail_zeros && str.include?(@dec_sep)
887
- if trim_trail_zeros && str.include?(@dec_sep) && str[-@rep_auto.size..-1]!=@rep_auto
888
- str.chop! while str[-1]==zero[0]
889
- str.chomp!(@dec_sep)
890
- #puts str
891
- end
892
-
893
-
894
- when :sci
895
-
896
- str << neutral.sign if @show_plus || neutral.sign!='+'
897
-
898
- if @show_base && @base_prefix
899
- b_prefix = @base_indicators[neutral.base]
900
- str << b_prefix if b_prefix
901
- end
902
-
903
- #zero = get_base_digits.digit_char(0).chr
904
- if @ndig==:exact
905
- neutral.sign = '+'
906
- neutral.dec_pos-=exp
907
- str << neutral.to_RepDec.getS(@rep_n, getRepDecOpt(neutral.base))
908
- else
909
- ns_digits = ''
910
-
911
- nd = neutral.digits.length
912
- if actual_mode==:fix
913
- nd -= neutral.dec_pos
914
- end
915
- if neutral.inexact? && @ndig>nd # assert no rep-dec.
916
- ns_digits = @non_sig*(@ndig-nd)
917
- end
918
-
919
- digits = neutral.digits + ns_digits
920
- str << ((integral_digits<1) ? zero : digits[0...integral_digits])
921
- str << @dec_sep
922
- str << digits[integral_digits...@ndig]
923
- pad_right =(@ndig+1-str.length)
924
- str << zero*pad_right if pad_right>0 && !neutral.inexact? # maybe we didn't have enought digits
925
- end
926
-
927
- #str = str.chomp(zero).chomp(@dec_sep) if trim_trail_zeros && str.include?(@dec_sep)
928
- if trim_trail_zeros && str.include?(@dec_sep) && str[-@rep_auto.size..-1]!=@rep_auto
929
- str.chop! while str[-1]==zero[0]
930
- str.chomp!(@dec_sep)
931
- #puts str
932
- end
933
-
934
- str << get_exp_char(neutral.base)
935
- str << exp.to_s
936
-
937
- end
938
-
939
- end
940
- end
941
-
942
- if @show_base && !@base_prefix
943
- b_prefix = @base_indicators[neutral.base]
944
- str << b_prefix if b_prefix
945
- end
946
-
947
-
948
- if @width>0 && @fill_char!=''
949
- l = @width - str.length
950
- if l>0
951
- case @adjust
952
- when :internal
953
- sign = ''
954
- if str[0,1]=='+' || str[0,1]=='-'
955
- sign = str[0,1]
956
- str = str[1...str.length]
957
- end
958
- str = sign + @fill_char*l + str
959
- when :center
960
- str = @fill_char*(l/2) + str + @fill_char*(l-l/2)
961
- when :right
962
- str = @fill_char*l + str
963
- when :left
964
- str = str + @fill_char*l
965
- end
966
- end
967
- end
968
-
969
- return str
970
- end
971
-
972
- # round a neutral numeral according to the format options
973
- def round!(neutral) # :nodoc:
974
- neutral.round! @ndig, @mode, @round
975
- end
976
-
977
- @@sci_fmt = nil
978
-
979
- def nio_read_formatted(txt) # :nodoc:
980
- txt = txt.dup
981
- num = nil
982
-
983
- base = nil
984
-
985
- base ||= get_base
986
-
987
- zero = get_base_digits(base).digit_char(0).chr
988
- txt.tr!(@non_sig,zero) # we don't simply remove it because it may be before the radix point
989
-
990
- exp = 0
991
- x_char = get_exp_char(base)
992
-
993
- exp_i = txt.index(x_char)
994
- exp_i = txt.index(x_char.downcase) if exp_i===nil
995
- if exp_i!=nil
996
- exp = txt[exp_i+1...txt.length].to_i
997
- txt = txt[0...exp_i]
998
- end
999
-
1000
-
1001
- opt = getRepDecOpt(base)
1002
- if @rep_in
1003
- #raise InvalidFormat,"Invalid numerical base" if base!=10
1004
- rd = RepDec.new # get_base not necessary: setS sets it from options
1005
- rd.setS txt, opt
1006
- num = rd.to_NeutralNum(opt.digits)
1007
- else
1008
- # to do: use RepDec.parse; then build NeutralNum directly
1009
- opt.set_delim '',''
1010
- opt.set_suffix ''
1011
- rd = RepDec.new # get_base not necessary: setS sets it from options
1012
- rd.setS txt, opt
1013
- num = rd.to_NeutralNum(opt.digits)
1014
- end
1015
- num.rounding = get_round
1016
- num.dec_pos += exp
1017
- return num
1018
- end
1019
-
1020
-
1021
- @@fmts = {
1022
- :def=>Fmt.new.freeze
1023
- }
1024
- # Returns the current default format.
1025
- def self.default
1026
- d = self[:def]
1027
- if block_given?
1028
- d = d.dup
1029
- yield d
1030
- end
1031
- d
1032
- end
1033
- # Defines the current default format.
1034
- def self.default=(fmt)
1035
- self[:def] = fmt
1036
- end
1037
- # Assigns a format to a name in the formats repository.
1038
- def self.[]=(tag,fmt_def)
1039
- @@fmts[tag.to_sym]=fmt_def.freeze
1040
- end
1041
- # Retrieves a named format from the repository.
1042
- def self.[](tag)
1043
- @@fmts[tag.to_sym]
1044
- end
1045
-
1046
- protected
1047
-
1048
- @@valid_properties = nil
1049
- ALIAS_PROPERTIES = {
1050
- :show_all_digits=>:all_digits,
1051
- :rounding_mode=>:round,
1052
- :approx_mode=>:approx,
1053
- :sci_digits=>:sci_format,
1054
- :non_signitificative_digits=>:non_sig,
1055
- :begin=>:rep_begin,
1056
- :end=>:rep_end,
1057
- :suffix=>:rep_auto,
1058
- :nreps=>:rep_n,
1059
- :read=>:rep_in
1060
- }
1061
- def set!(properties={}) # :nodoc:
1062
-
1063
-
1064
- @@valid_properties ||= instance_variables.collect{|v| v[1..-1].to_sym}
1065
-
1066
-
1067
- properties.each do |k,v|
1068
- al = ALIAS_PROPERTIES[k]
1069
- if al
1070
- properties[al] = v
1071
- properties.delete k
1072
- elsif !@@valid_properties.include?(k)
1073
- raise InvalidOption, "Invalid option: #{k}"
1074
- end
1075
- end
1076
-
1077
-
1078
- if properties[:grp_sep].nil? && !properties[:dec_sep].nil? && properties[:dec_sep]!=@dec_sep && properties[:dec_sep]==@grp_sep
1079
- properties[:grp_sep] = properties[:dec_sep]=='.' ? ',' : '.'
1080
- end
1081
-
1082
- if properties[:all_digits].nil? && (properties[:ndig] || properties[:mode])
1083
- ndig = properties[:ndig] || @ndig
1084
- mode = properties[:mode] || @mode
1085
- properties[:all_digits] = ndig!=:exact && mode!=:gen
1086
- end
1087
-
1088
- if !properties[:all_digits].nil? && properties[:non_sig].nil?
1089
- properties[:non_sig] = '' unless properties[:all_digits]
1090
- elsif !properties[:non_sig].nil? && properties[:all_digits].nil?
1091
- properties[:all_digits] = true if properties[:non_sig]!=''
1092
- end
1093
-
1094
- if !properties[:base_radix].nil? || !properties[:base_uppercase].nil?
1095
- base = properties[:base_radix] || @base_radix
1096
- uppercase = properties[:base_uppercase] || @base_uppercase
1097
- properties[:base_digits] = DigitsDef.base(base, !uppercase)
1098
- end
1099
-
1100
-
1101
- properties.each do |k,v|
1102
- instance_variable_set "@#{k}", v unless v.nil?
1103
- end
1104
-
1105
- self
1106
- end
1107
-
1108
- def set(properties={}) # :nodoc:
1109
- self.dup.set!(properties)
1110
- end
1111
-
1112
- def use_scientific?(neutral,exp) # :nodoc:
1113
- nd = @ndig.kind_of?(Numeric) ? @ndig : [neutral.digits.length,10].max
1114
- if @@sci_fmt==:hp
1115
- puts " #{nd} ndpos=#{neutral.dec_pos} ndlen=#{neutral.digits.length}"
1116
- neutral.dec_pos>nd || ([neutral.digits.length,nd].min-neutral.dec_pos)>nd
1117
- else
1118
- exp<-4 || exp>=nd
1119
- end
1120
- end
1121
-
1122
- def getRepDecOpt(base=nil) # :nodoc:
1123
- rd_opt = RepDec::Opt.new
1124
- rd_opt.begin_rep = @rep_begin
1125
- rd_opt.end_rep = @rep_end
1126
- rd_opt.auto_rep = @rep_auto
1127
- rd_opt.dec_sep = @dec_sep
1128
- rd_opt.grp_sep = @grp_sep
1129
- rd_opt.grp = @grp
1130
- rd_opt.inf_txt = @inf_txt
1131
- rd_opt.nan_txt = @nan_txt
1132
- rd_opt.set_digits(get_base_digits(base))
1133
- # if base && (base != get_base_digits.radix)
1134
- # rd_opt.set_digits(get_base_digits(base))
1135
- # else
1136
- # rd_opt.set_digits get_base_digits
1137
- # end
1138
- return rd_opt
1139
- end
1140
-
1141
- def group(digits) # :nodoc:
1142
- RepDec.group_digits(digits, getRepDecOpt)
1143
- end
1144
-
1145
- end
1146
-
1147
- # This is a mix-in module to add formatting capabilities no numerical classes.
1148
- # A class that includes this module should provide the methods
1149
- # nio_write_neutral(fmt):: an instance method to write the value to
1150
- # a neutral numeral. The format is passed so that
1151
- # the base, for example, is available.
1152
- # nio_read_neutral(neutral):: a class method to create a value from a neutral
1153
- # numeral.
1154
- module Formattable
1155
-
1156
- # This is the method available in all formattable objects
1157
- # to format the value into a text string according
1158
- # to the optional format passed.
1159
- def nio_write(fmt=Fmt.default)
1160
- neutral = nio_write_neutral(fmt)
1161
- fmt.nio_write_formatted(neutral)
1162
- end
1163
-
1164
- module ClassMethods
1165
- # This is the method available in all formattable clases
1166
- # to read a formatted value from a text string into
1167
- # a value the class, according to the optional format passed.
1168
- def nio_read(txt,fmt=Fmt.default)
1169
- neutral = fmt.nio_read_formatted(txt)
1170
- nio_read_neutral neutral
1171
- end
1172
- end
1173
-
1174
- # Round a formattable object according to the rounding mode and
1175
- # precision of a format.
1176
- def nio_round(fmt=Fmt.default)
1177
- neutral = nio_write_neutral(fmt)
1178
- fmt.round! neutral
1179
- self.class.nio_read_neutral neutral
1180
- end
1181
-
1182
- def self.append_features(mod) # :nodoc:
1183
- super
1184
- mod.extend ClassMethods
1185
- end
1186
-
1187
- end
1188
-
1189
- Fmt[:comma] = Fmt.sep(',','.')
1190
- Fmt[:comma_th] = Fmt.sep(',','.',[3])
1191
- Fmt[:dot] = Fmt.sep('.',',')
1192
- Fmt[:dot_th] = Fmt.sep('.',',',[3])
1193
- Fmt[:code] = Fmt.new.prec(20) # don't use :exact to avoid repeating numerals
1194
-
1195
- class Fmt
1196
- # Intermediate conversion format for simplified conversion
1197
- CONV_FMT = Fmt.prec(:exact).rep('<','>','...',0).approx_mode(:simplify)
1198
- # Intermediate conversion format for exact conversion
1199
- CONV_FMT_STRICT = Fmt.prec(:exact).rep('<','>','...',0).approx_mode(:exact)
1200
- # Numerical conversion: converts the quantity +x+ to an object
1201
- # of class +type+.
1202
- #
1203
- # The third parameter is the kind of conversion:
1204
- # [<tt>:approx</tt>]
1205
- # Tries to find an approximate simpler value if possible for inexact
1206
- # numeric types. This is the default. This is slower in general and
1207
- # may take some seconds in some cases.
1208
- # [<tt>:exact</tt>]
1209
- # Performs a conversion as exact as possible.
1210
- # The third parameter is true for approximate
1211
- # conversion (inexact values are simplified if possible) and false
1212
- # for conversions as exact as possible.
1213
- def Fmt.convert(x, type, mode=:approx)
1214
- fmt = mode==:approx ? CONV_FMT : CONV_FMT_STRICT
1215
- # return x.prec(type)
1216
- if !(x.is_a?(type))
1217
- # return type.nio_read(x.nio_write(fmt),fmt)
1218
-
1219
- x = x.nio_write_neutral(fmt)
1220
- x = type.nio_read_neutral(x)
1221
-
1222
- end
1223
- x
1224
- end
1225
- end
1226
-
1227
- module_function
1228
-
1229
- def nio_float_to_bigdecimal(x,prec) # :nodoc:
1230
- if prec.nil?
1231
- x = Nio.convert(x,BigDecimal,:approx)
1232
- elsif prec==:exact
1233
- x = Nio.convert(x,BigDecimal,:exact)
1234
- else
1235
- x = BigDecimal(x.nio_write(Nio::Fmt.new.prec(prec,:sig)))
1236
- end
1237
- x
1238
- end
1239
-
1240
-
1241
- module Clinger # :nodoc: all
1242
- module_function
1243
-
1244
- def algM(f,e,round_mode,eb=10,beta=Float::RADIX,n=Float::MANT_DIG,min_e=Float::MIN_EXP-Float::MANT_DIG,max_e=Float::MAX_EXP-Float::MANT_DIG)
1245
-
1246
- if e<0
1247
- u,v,k = f,eb**(-e),0
1248
- else
1249
- u,v,k = f*(eb**e),1,0
1250
- end
1251
-
1252
- loop do
1253
- x = u.div(v)
1254
- # overflow if k>=max_e
1255
- if (x>=beta**(n-1) && x<beta**n) || k==min_e || k==max_e
1256
- return ratio_float(u,v,k,round_mode,beta,n)
1257
- elsif x<beta**(n-1)
1258
- u *= beta
1259
- k -= 1
1260
- elsif x>=beta**n
1261
- v *= beta
1262
- k += 1
1263
- end
1264
- end
1265
-
1266
- end
1267
-
1268
- def ratio_float(u,v,k,round_mode,beta=Float::RADIX,n=Float::MANT_DIG)
1269
- q,r = u.divmod(v)
1270
- v_r = v-r
1271
- z = Math.ldexp(q,k)
1272
- if r<v_r
1273
- z
1274
- elsif r>v_r
1275
- nextfloat z
1276
- elsif (round_mode==:even && q.even?) || (round_mode==:zero)
1277
- z
1278
- else
1279
- nextfloat z
1280
- end
1281
- end
1282
-
1283
- # valid only for non-negative x
1284
- def nextfloat(x)
1285
- f,e = Math.frexp(x)
1286
- e = Float::MIN_EXP if f==0
1287
- e = [Float::MIN_EXP,e].max
1288
- dx = Math.ldexp(1,e-Float::MANT_DIG) #Math.ldexp(Math.ldexp(1.0,-Float::MANT_DIG),e)
1289
- if f==(1.0 - Math.ldexp(1,-Float::MANT_DIG))
1290
- x + dx*2
1291
- else
1292
- x + dx
1293
- end
1294
- end
1295
-
1296
- # valid only for non-negative x
1297
- def prevfloat(x)
1298
- f,e = Math.frexp(x)
1299
- e = Float::MIN_EXP if f==0
1300
- e = [Float::MIN_EXP,e].max
1301
- dx = Math.ldexp(1,e-Float::MANT_DIG) #Math.ldexp(Math.ldexp(1.0,-Float::MANT_DIG),e)
1302
- if e==Float::MIN_EXP || f!=0.5 #0.5==Math.ldexp(2**(bits-1),-Float::MANT_DIG)
1303
- x - dx
1304
- else
1305
- x - dx/2 # x - Math.ldexp(Math.ldexp(1.0,-Float::MANT_DIG),e-1)
1306
- end
1307
- end
1308
-
1309
- end
1310
-
1311
- module BurgerDybvig # :nodoc: all
1312
- module_function
1313
-
1314
- def float_to_digits(v,f,e,round_mode,min_e,p,b,_B)
1315
-
1316
- case round_mode
1317
- when :even
1318
- roundl = roundh = f.even?
1319
- when :inf
1320
- roundl = true
1321
- roundh = false
1322
- when :zero
1323
- roundl = false
1324
- roundh = true
1325
- else
1326
- # here we don't assume any rounding in the floating point numbers
1327
- # the result is valid for any rounding but may produce more digits
1328
- # than stricly necessary for specifica rounding modes.
1329
- roundl = false
1330
- roundh = false
1331
- end
1332
-
1333
- if e >= 0
1334
- if f != exptt(b,p-1)
1335
- be = exptt(b,e)
1336
- r,s,m_p,m_m,k = scale(f*be*2,2,be,be,0,_B,roundl ,roundh,v)
1337
- else
1338
- be = exptt(b,e)
1339
- be1 = be*b
1340
- r,s,m_p,m_m,k = scale(f*be1*2,b*2,be1,be,0,_B,roundl ,roundh,v)
1341
- end
1342
- else
1343
- if e==min_e or f != exptt(b,p-1)
1344
- r,s,m_p,m_m,k = scale(f*2,exptt(b,-e)*2,1,1,0,_B,roundl ,roundh,v)
1345
- else
1346
- r,s,m_p,m_m,k = scale(f*b*2,exptt(b,1-e)*2,b,1,0,_B,roundl ,roundh,v)
1347
- end
1348
- end
1349
- [k]+generate(r,s,m_p,m_m,_B,roundl ,roundh)
1350
- end
1351
-
1352
- def scale(r,s,m_p,m_m,k,_B,low_ok ,high_ok,v)
1353
- return scale2(r,s,m_p,m_m,k,_B,low_ok ,high_ok) if v==0
1354
- est = (logB(_B,v)-1E-10).ceil.to_i
1355
- if est>=0
1356
- fixup(r,s*exptt(_B,est),m_p,m_m,est,_B,low_ok,high_ok)
1357
- else
1358
- sc = exptt(_B,-est)
1359
- fixup(r*sc,s,m_p*sc,m_m*sc,est,_B,low_ok,high_ok)
1360
- end
1361
- end
1362
-
1363
- def fixup(r,s,m_p,m_m,k,_B,low_ok,high_ok)
1364
- if (high_ok ? (r+m_p >= s) : (r+m_p > s)) # too low?
1365
- [r,s*_B,m_p,m_m,k+1]
1366
- else
1367
- [r,s,m_p,m_m,k]
1368
- end
1369
- end
1370
-
1371
- def scale2(r,s,m_p,m_m,k,_B,low_ok ,high_ok)
1372
- loop do
1373
- if (high_ok ? (r+m_p >= s) : (r+m_p > s)) # k is too low
1374
- s *= _B
1375
- k += 1
1376
- elsif (high_ok ? ((r+m_p)*_B<s) : ((r+m_p)*_B<=s)) # k is too high
1377
- r *= _B
1378
- m_p *= _B
1379
- m_m *= _B
1380
- k -= 1
1381
- else
1382
- break
1383
- end
1384
- end
1385
- [r,s,m_p,m_m,k]
1386
- end
1387
-
1388
- def generate(r,s,m_p,m_m,_B,low_ok ,high_ok)
1389
- list = []
1390
- loop do
1391
- d,r = (r*_B).divmod(s)
1392
- m_p *= _B
1393
- m_m *= _B
1394
- tc1 = low_ok ? (r<=m_m) : (r<m_m)
1395
- tc2 = high_ok ? (r+m_p >= s) : (r+m_p > s)
1396
-
1397
- if not tc1
1398
- if not tc2
1399
- list << d
1400
- else
1401
- list << d+1
1402
- break
1403
- end
1404
- else
1405
- if not tc2
1406
- list << d
1407
- break
1408
- else
1409
- if r*2 < s
1410
- list << d
1411
- break
1412
- else
1413
- list << d+1
1414
- break
1415
- end
1416
- end
1417
- end
1418
-
1419
- end
1420
- list
1421
- end
1422
-
1423
- $exptt_table = Array.new(326)
1424
- (0...326).each{|i| $exptt_table[i]=10**i}
1425
- def exptt(_B, k)
1426
- if _B==10 && k>=0 && k<326
1427
- $exptt_table[k]
1428
- else
1429
- _B**k
1430
- end
1431
- end
1432
-
1433
- $logB_table = Array.new(37)
1434
- (2...37).each{|b| $logB_table[b]=1.0/Math.log(b)}
1435
- def logB(_B, x)
1436
- if _B>=2 && _B<37
1437
- Math.log(x)*$logB_table[_B]
1438
- else
1439
- Math.log(x)/Math.log(_B)
1440
- end
1441
- end
1442
-
1443
- def float_to_digits_max(v,f,e,round_mode,min_e,p,b,_B)
1444
-
1445
- case round_mode
1446
- when :even
1447
- roundl = roundh = f.even?
1448
- when :inf
1449
- roundl = true
1450
- roundh = false
1451
- when :zero
1452
- roundl = false
1453
- roundh = true
1454
- else
1455
- # here we don't assume any rounding in the floating point numbers
1456
- # the result is valid for any rounding but may produce more digits
1457
- # than stricly necessary for specifica rounding modes.
1458
- roundl = false
1459
- roundh = false
1460
- end
1461
-
1462
- if e >= 0
1463
- if f != exptt(b,p-1)
1464
- be = exptt(b,e)
1465
- r,s,m_p,m_m,k = scale(f*be*2,2,be,be,0,_B,roundl ,roundh,v)
1466
- else
1467
- be = exptt(b,e)
1468
- be1 = be*b
1469
- r,s,m_p,m_m,k = scale(f*be1*2,b*2,be1,be,0,_B,roundl ,roundh,v)
1470
- end
1471
- else
1472
- if e==min_e or f != exptt(b,p-1)
1473
- r,s,m_p,m_m,k = scale(f*2,exptt(b,-e)*2,1,1,0,_B,roundl ,roundh,v)
1474
- else
1475
- r,s,m_p,m_m,k = scale(f*b*2,exptt(b,1-e)*2,b,1,0,_B,roundl ,roundh,v)
1476
- end
1477
- end
1478
- [k]+generate_max(r,s,m_p,m_m,_B,roundl ,roundh)
1479
- end
1480
-
1481
- def generate_max(r,s,m_p,m_m,_B,low_ok ,high_ok)
1482
- list = [false]
1483
- loop do
1484
- d,r = (r*_B).divmod(s)
1485
- m_p *= _B
1486
- m_m *= _B
1487
-
1488
- list << d
1489
-
1490
- tc1 = low_ok ? (r<=m_m) : (r<m_m)
1491
- tc2 = high_ok ? (r+m_p >= s) : (r+m_p > s)
1492
-
1493
- if tc1 && tc2
1494
- list[0] = true if r*2 >= s
1495
- break
1496
- end
1497
- end
1498
- list
1499
- end
1500
-
1501
- end
1502
-
1503
- end
1504
-
1505
- class Float
1506
- include Nio::Formattable
1507
- def self.nio_read_neutral(neutral)
1508
- x = nil
1509
-
1510
- honor_rounding = true
1511
-
1512
- if neutral.special?
1513
- case neutral.special
1514
- when :nan
1515
- x = 0.0/0.0
1516
- when :inf
1517
- x = (neutral.sign=='-' ? -1.0 : +1.0)/0.0
1518
- end
1519
- elsif neutral.rep_pos<neutral.digits.length
1520
-
1521
- x,y = neutral.to_RepDec.getQ
1522
- x = Float(x)/y
1523
-
1524
- else
1525
- nd = neutral.base==10 ? Float::DIG : ((Float::MANT_DIG-1)*Math.log(2)/Math.log(neutral.base)).floor
1526
- k = neutral.dec_pos-neutral.digits.length
1527
- if !honor_rounding && (neutral.digits.length<=nd && k.abs<=15)
1528
- x = neutral.digits.to_i(neutral.base).to_f
1529
- if k<0
1530
- x /= Float(neutral.base**-k)
1531
- else
1532
- x *= Float(neutral.base**k)
1533
- end
1534
- x = -x if neutral.sign=='-'
1535
- elsif !honor_rounding && (k>0 && (k+neutral.digits.length < 2*nd))
1536
- j = k-neutral.digits.length
1537
- x = neutral.digits.to_i(neutral.base).to_f * Float(neutral.base**(j))
1538
- x *= Float(neutral.base**(k-j))
1539
- x = -x if neutral.sign=='-'
1540
- elsif neutral.base.modulo(Float::RADIX)==0
1541
-
1542
- f = neutral.digits.to_i(neutral.base)
1543
- e = neutral.dec_pos-neutral.digits.length
1544
-
1545
- rounding = neutral.rounding
1546
-
1547
- x = Nio::Clinger::algM(f,e,rounding,neutral.base,Float::RADIX,Float::MANT_DIG,Float::MIN_EXP-Float::MANT_DIG,Float::MAX_EXP-Float::MANT_DIG)
1548
- x = -x if neutral.sign=='-'
1549
-
1550
- else
1551
-
1552
- f = neutral.digits.to_i(neutral.base)
1553
- e = neutral.dec_pos-neutral.digits.length
1554
-
1555
- rounding = neutral.rounding
1556
-
1557
- x = Nio::Clinger::algM(f,e,rounding,neutral.base,Float::RADIX,Float::MANT_DIG,Float::MIN_EXP-Float::MANT_DIG,Float::MAX_EXP-Float::MANT_DIG)
1558
- x = -x if neutral.sign=='-'
1559
-
1560
- end
1561
- end
1562
-
1563
- return x
1564
- end
1565
- def nio_write_neutral(fmt)
1566
- neutral = Nio::NeutralNum.new
1567
- x = self
1568
-
1569
- if x.nan?
1570
- neutral.set_special(:nan)
1571
- elsif x.infinite?
1572
- neutral.set_special(:inf, x<0 ? '-' : '+')
1573
- else
1574
- converted = false
1575
- if fmt.get_ndig==:exact && fmt.get_approx==:simplify
1576
-
1577
- if x!=0
1578
- q = x.nio_r(Nio::Tolerance.decimals(Float::DIG,:sig))
1579
- if q!=0
1580
- neutral = q.nio_write_neutral(fmt)
1581
- converted = true if neutral.digits.length<=Float::DIG
1582
- end
1583
- end
1584
-
1585
- elsif fmt.get_approx==:exact
1586
- neutral = x.nio_xr.nio_write_neutral(fmt)
1587
- converted = true
1588
- end
1589
- if !converted
1590
- if fmt.get_base==10 && false
1591
- txt = format "%.*e",Float::DECIMAL_DIG-1,x # note that spec. e output precision+1 significant digits
1592
-
1593
- sign = '+'
1594
- if txt[0,1]=='-'
1595
- sign = '-'
1596
- txt = txt[1...txt.length]
1597
- end
1598
- exp = 0
1599
- x_char = fmt.get_exp_char(fmt.get_base)
1600
-
1601
- exp_i = txt.index(x_char)
1602
- exp_i = txt.index(x_char.downcase) if exp_i===nil
1603
- if exp_i!=nil
1604
- exp = txt[exp_i+1...txt.length].to_i
1605
- txt = txt[0...exp_i]
1606
- end
1607
-
1608
- dec_pos = txt.index '.'
1609
- if dec_pos==nil
1610
- dec_pos = txt.length
1611
- else
1612
- txt[dec_pos]=''
1613
- end
1614
- dec_pos += exp
1615
- neutral.set sign, txt, dec_pos, nil, fmt.get_base_digits(10), true, fmt.get_round
1616
-
1617
- converted = true
1618
- end
1619
- end
1620
- if !converted
1621
-
1622
- sign = x<0 ? '-' : '+'
1623
- x = -x if sign=='-'
1624
- f,e = Math.frexp(x)
1625
- if e < Float::MIN_EXP
1626
- # denormalized number
1627
- f = Math.ldexp(f,e-Float::MIN_EXP+Float::MANT_DIG)
1628
- e = Float::MIN_EXP-Float::MANT_DIG
1629
- else
1630
- # normalized number
1631
- f = Math.ldexp(f,Float::MANT_DIG)
1632
- e -= Float::MANT_DIG
1633
- end
1634
- f = f.to_i
1635
- inexact = true
1636
-
1637
- rounding = fmt.get_round
1638
-
1639
- if fmt.get_all_digits?
1640
- # use as many digits as possible
1641
- dec_pos,r,*digits = Nio::BurgerDybvig::float_to_digits_max(x,f,e,rounding,Float::MIN_EXP-Float::MANT_DIG,Float::MANT_DIG,Float::RADIX,fmt.get_base)
1642
- inexact = :roundup if r
1643
- else
1644
- # use as few digits as possible
1645
- dec_pos,*digits = Nio::BurgerDybvig::float_to_digits(x,f,e,rounding,Float::MIN_EXP-Float::MANT_DIG,Float::MANT_DIG,Float::RADIX,fmt.get_base)
1646
- end
1647
- txt = ''
1648
- digits.each{|d| txt << fmt.get_base_digits.digit_char(d)}
1649
- neutral.set sign, txt, dec_pos, nil, fmt.get_base_digits, inexact, fmt.get_round
1650
-
1651
- end
1652
- end
1653
-
1654
- return neutral
1655
- end
1656
- end
1657
-
1658
- class Numeric
1659
- unless method_defined?(:even?)
1660
- def even?
1661
- self.modulo(2)==0
1662
- end
1663
- end
1664
- unless method_defined?(:odd?)
1665
- def odd?
1666
- self.modulo(2)!=0
1667
- end
1668
- end
1669
- end
1670
-
1671
- class Integer
1672
- include Nio::Formattable
1673
- def self.nio_read_neutral(neutral)
1674
- x = nil
1675
-
1676
- if neutral.special?
1677
- raise Nio::InvalidFormat,"Invalid integer numeral"
1678
- elsif neutral.rep_pos<neutral.digits.length
1679
- return Rational.nio_read_neutral(neutral).to_i
1680
- else
1681
- digits = neutral.digits
1682
-
1683
- if neutral.dec_pos <= 0
1684
- digits = '0'
1685
- elsif neutral.dec_pos <= digits.length
1686
- digits = digits[0...neutral.dec_pos]
1687
- else
1688
- digits = digits + '0'*(neutral.dec_pos-digits.length)
1689
- end
1690
-
1691
- x = digits.to_i(neutral.base)
1692
- # this was formely needed because we didn't adust the digits
1693
- # if neutral.dec_pos != neutral.digits.length
1694
- # # with rational included, negative powers of ten are rational numbers
1695
- # x = (x*((neutral.base)**(neutral.dec_pos-neutral.digits.length))).to_i
1696
- # end
1697
- x = -x if neutral.sign=='-'
1698
- end
1699
-
1700
- return x
1701
- end
1702
- def nio_write_neutral(fmt)
1703
- neutral = Nio::NeutralNum.new
1704
- x = self
1705
-
1706
- sign = x<0 ? '-' : '+'
1707
- txt = x.abs.to_s(fmt.get_base)
1708
- dec_pos = rep_pos = txt.length
1709
- neutral.set sign, txt, dec_pos, nil, fmt.get_base_digits, false ,fmt.get_round
1710
-
1711
- return neutral
1712
- end
1713
- end
1714
-
1715
- class Rational
1716
- include Nio::Formattable
1717
- def self.nio_read_neutral(neutral)
1718
- x = nil
1719
-
1720
- if neutral.special?
1721
- case neutral.special
1722
- when :nan
1723
- x = Rational(0,0)
1724
- when :inf
1725
- x = Rational((neutral.sign=='-' ? -1 : +1),0)
1726
- end
1727
- else
1728
- x = Rational(*neutral.to_RepDec.getQ)
1729
- end
1730
-
1731
- return x
1732
- end
1733
- def nio_write_neutral(fmt)
1734
- neutral = Nio::NeutralNum.new
1735
- x = self
1736
-
1737
- if x.denominator==0
1738
- if x.numerator>0
1739
- neutral.set_special(:inf)
1740
- elsif x.numerator<0
1741
- neutral.set_special(:inf,'-')
1742
- else
1743
- neutral.set_special(:nan)
1744
- end
1745
- else
1746
- if fmt.get_base==10
1747
- rd = Nio::RepDec.new.setQ(x.numerator,x.denominator)
1748
- else
1749
- opt = Nio::RepDec::DEF_OPT.dup.set_digits(fmt.get_base_digits)
1750
- rd = Nio::RepDec.new.setQ(x.numerator,x.denominator, opt)
1751
- end
1752
- neutral = rd.to_NeutralNum(fmt.get_base_digits)
1753
- neutral.rounding = fmt.get_round
1754
- end
1755
-
1756
- return neutral
1757
- end
1758
- end
1759
-
1760
- if defined? BigDecimal
1761
- class BigDecimal
1762
- include Nio::Formattable
1763
- def self.nio_read_neutral(neutral)
1764
- x = nil
1765
-
1766
- if neutral.special?
1767
- case neutral.special
1768
- when :nan
1769
- x = BigDecimal('NaN') # BigDecimal("0")/0
1770
- when :inf
1771
- x = BigDecimal(neutral.sign=='-' ? '-1.0' : '+1.0')/0
1772
- end
1773
- elsif neutral.rep_pos<neutral.digits.length
1774
-
1775
- x,y = neutral.to_RepDec.getQ
1776
- x = BigDecimal(x.to_s)/y
1777
-
1778
- else
1779
- if neutral.base==10
1780
- #x = BigDecimal(neutral.digits)
1781
- #x *= BigDecimal("1E#{(neutral.dec_pos-neutral.digits.length)}")
1782
- #x = -x if neutral.sign=='-'
1783
- str = neutral.sign
1784
- str += neutral.digits
1785
- str += "E#{(neutral.dec_pos-neutral.digits.length)}"
1786
- x = BigDecimal(str)
1787
- else
1788
- x = BigDecimal(neutral.digits.to_i(neutral.base).to_s)
1789
- x *= BigDecimal(neutral.base.to_s)**(neutral.dec_pos-neutral.digits.length)
1790
- x = -x if neutral.sign=='-'
1791
- end
1792
- end
1793
-
1794
- return x
1795
- end
1796
- def nio_write_neutral(fmt)
1797
- neutral = Nio::NeutralNum.new
1798
- x = self
1799
-
1800
- if x.nan?
1801
- neutral.set_special(:nan)
1802
- elsif x.infinite?
1803
- neutral.set_special(:inf, x<0 ? '-' : '+')
1804
- else
1805
- converted = false
1806
- if fmt.get_ndig==:exact && fmt.get_approx==:simplify
1807
-
1808
- prc = [x.precs[0],20].max
1809
- neutral = x.nio_r(Nio::BigTolerance.decimals(prc,:sig)).nio_write_neutral(fmt)
1810
- converted = true if neutral.digits.length<prc
1811
-
1812
- elsif fmt.get_approx==:exact && fmt.get_base!=10
1813
- neutral = x.nio_xr.nio_write_neutral(fmt)
1814
- converted = true
1815
- end
1816
- if !converted
1817
- if fmt.get_base==10
1818
- txt = x.to_s
1819
-
1820
- sign = '+'
1821
- if txt[0,1]=='-'
1822
- sign = '-'
1823
- txt = txt[1...txt.length]
1824
- end
1825
- exp = 0
1826
- x_char = fmt.get_exp_char(fmt.get_base)
1827
-
1828
- exp_i = txt.index(x_char)
1829
- exp_i = txt.index(x_char.downcase) if exp_i===nil
1830
- if exp_i!=nil
1831
- exp = txt[exp_i+1...txt.length].to_i
1832
- txt = txt[0...exp_i]
1833
- end
1834
-
1835
- dec_pos = txt.index '.'
1836
- if dec_pos==nil
1837
- dec_pos = txt.length
1838
- else
1839
- txt[dec_pos]=''
1840
- end
1841
- dec_pos += exp
1842
- neutral.set sign, txt, dec_pos, nil, fmt.get_base_digits(10), true, fmt.get_round
1843
-
1844
- converted = true
1845
- end
1846
- end
1847
- if !converted
1848
-
1849
- min_prec = 24
1850
- min_exp = -1000
1851
- s,f,b,e = x.split
1852
- e -= f.size
1853
- sign = s<0 ? '-' : '+'
1854
- x = -x if sign=='-'
1855
- f_i = f.to_i
1856
- prc = [x.precs[0],min_prec].max
1857
- f_i *= 10**(prc-f.size)
1858
- e -= (prc-f.size)
1859
-
1860
- inexact = true
1861
-
1862
- rounding = fmt.get_round
1863
-
1864
- if fmt.get_all_digits?
1865
- # use as many digits as possible
1866
- dec_pos,r,*digits = Nio::BurgerDybvig::float_to_digits_max(x,f_i,e,rounding,[e,min_exp].min,prc,b,fmt.get_base)
1867
- inexact = :roundup if r
1868
- else
1869
- # use as few digits as possible
1870
- dec_pos,*digits = Nio::BurgerDybvig::float_to_digits(x,f_i,e,rounding,[e,min_exp].min,prc,b,fmt.get_base)
1871
- end
1872
- txt = ''
1873
- digits.each{|d| txt << fmt.get_base_digits.digit_char(d)}
1874
- neutral.set sign, txt, dec_pos, nil, fmt.get_base_digits, inexact, fmt.get_round
1875
-
1876
- end
1877
- end
1878
-
1879
- return neutral
1880
- end
1881
- end
1882
- end
1883
-
1
+ # Formatting numbers as text
2
+
3
+ # Copyright (C) 2003-2005, Javier Goizueta <javier@goizueta.info>
4
+ #
5
+ # This program is free software; you can redistribute it and/or
6
+ # modify it under the terms of the GNU General Public License
7
+ # as published by the Free Software Foundation; either version 2
8
+ # of the License, or (at your option) any later version.
9
+
10
+
11
+ require 'nio/tools'
12
+
13
+ require 'nio/repdec'
14
+
15
+ require 'nio/rtnlzr'
16
+
17
+ require 'rational'
18
+
19
+ require 'bigdecimal'
20
+
21
+ module Nio
22
+
23
+ # positional notation, unformatted numeric literal: used as intermediate form
24
+ class NeutralNum
25
+ include StateEquivalent
26
+ def initialize(s='',d='',p=nil,r=nil,dgs=DigitsDef.base(10), inexact=false, round=:inf)
27
+ set s,d,p,r,dgs,dgs, inexact, round
28
+ end
29
+ attr_reader :sign, :digits, :dec_pos, :rep_pos, :special, :inexact, :rounding
30
+ attr_writer :sign, :digits, :dec_pos, :rep_pos, :special, :inexact, :rounding
31
+
32
+ # set number
33
+ def set(s,d,p=nil,r=nil,dgs=DigitsDef.base(10),inexact=false,rounding=:inf,normalize=true)
34
+ @sign = s # sign: '+','-',''
35
+ @digits = d # digits string
36
+ @dec_pos = p==nil ? d.length : p # position of decimal point: 0=before first digit...
37
+ @rep_pos = r==nil ? d.length : r # first repeated digit (0=first digit...)
38
+ @dgs = dgs
39
+ @base = @dgs.radix
40
+ @inexact = inexact
41
+ @special = nil
42
+ @rounding = rounding
43
+ trimZeros unless inexact
44
+ self
45
+ end
46
+ # set infinite (:inf) and invalid (:nan) numbers
47
+ def set_special(s,sgn='') # :inf, :nan
48
+ @special = s
49
+ @sign = sgn
50
+ self
51
+ end
52
+
53
+ def base
54
+ @base
55
+ end
56
+ def base_digits
57
+ @dgs
58
+ end
59
+ def base_digits=(dd)
60
+ @dgs = dd
61
+ @base = @dgs.radix
62
+ end
63
+ def base=(b)
64
+ @dgs = DigitsDef.base(b)
65
+ @base=@dgs.radix
66
+ end
67
+
68
+ # check for special numbers (which have only special and sign attributes)
69
+ def special?
70
+ special != nil
71
+ end
72
+
73
+ # check for special numbers (which have only special and sign attributes)
74
+ def inexact?
75
+ @inexact
76
+ end
77
+
78
+ def dup
79
+ n = NeutralNum.new
80
+ if special?
81
+ n.set_special @special.dup, @sign.dup
82
+ else
83
+ #n.set @sign.dup, @digits.dup, @dec_pos.dup, @rep_pos.dup, @dgs.dup
84
+ # in Ruby 1.6.8 Float,BigNum,Fixnum doesn't respond to dup
85
+ n.set @sign.dup, @digits.dup, @dec_pos, @rep_pos, @dgs.dup, @inexact, @rounding
86
+ end
87
+ return n
88
+ end
89
+
90
+ def zero?
91
+ z = false
92
+ if !special
93
+ if digits==''
94
+ z = true
95
+ else
96
+ z = true
97
+ for i in (0...@digits.length)
98
+ if dig_value(i)!=0
99
+ z = false
100
+ break
101
+ end
102
+ end
103
+ end
104
+ end
105
+ z
106
+ end
107
+
108
+ def round!(n, mode=:fix, dir=nil)
109
+ dir ||= rounding
110
+ trimLeadZeros
111
+ if n==:exact
112
+ return unless @inexact
113
+ n = @digits.size
114
+ end
115
+
116
+ n += @dec_pos if mode==:fix
117
+ n = [n,@digits.size].min if @inexact
118
+
119
+ adj = 0
120
+ dv = :tie
121
+ if @inexact && n==@digits.size
122
+ dv = @inexact==:roundup ? :hi : :lo
123
+ else
124
+ v = dig_value(n)
125
+ v2 = 2*v
126
+ if v2 < @base # v<((@base+1)/2)
127
+ dv = :lo
128
+ elsif v2 > @base # v>(@base/2)
129
+ dv = :hi
130
+ else
131
+
132
+ (n+1...@digits.length).each do |i|
133
+ if dig_value(i)>0
134
+ dv = :hi
135
+ break
136
+ end
137
+ end
138
+
139
+ dv = :hi if dv==:tie && @rep_pos<=n
140
+ end
141
+ end
142
+
143
+ if dv==:hi
144
+ adj = +1
145
+ elsif dv==:tie
146
+ if dir==:inf # towards nearest +/-infinity
147
+ adj = +1
148
+ elsif dir==:even # to nearest even digit (IEEE unbiased rounding)
149
+ adj = +1 if (dig_value(n-1)%2)!=0
150
+ elsif dir==:zero # towards zero
151
+ adj=0
152
+ # elsif dir==:odd
153
+ # adj = +1 unless (dig_value(n-1)%2)!=0
154
+ end
155
+ end
156
+
157
+ if n>@digits.length
158
+ (@digits.length...n).each do |i|
159
+ @digits << dig_char(dig_value(i))
160
+ @rep_pos += 1
161
+ end
162
+ end
163
+
164
+ prefix = ''
165
+ i = n-1
166
+ while adj!=0
167
+ v = dig_value(i)
168
+ v += adj
169
+ adj = 0
170
+ if v<0
171
+ v += @base
172
+ adj = -1
173
+ elsif v>=@base
174
+ v -= @base
175
+ adj = +1
176
+ end
177
+ if i<0
178
+ prefix = dig_char(v)+prefix
179
+ elsif i<@digits.length
180
+ @digits[i] = dig_char(v)
181
+ end
182
+ i += -1
183
+ end
184
+
185
+ if n<0
186
+ @digits = ""
187
+ else
188
+ @digits = @digits[0...n]
189
+ end
190
+ @rep_pos = @digits.length
191
+
192
+ if prefix!=''
193
+ @digits = prefix + @digits
194
+ @dec_pos += prefix.length
195
+ @rep_pos += prefix.length
196
+ end
197
+
198
+
199
+ end
200
+
201
+ def round(n, mode=:fix, dir=nil)
202
+ dir ||= rounding
203
+ nn = dup
204
+ nn.round!(n,mode,dir)
205
+ return nn
206
+ end
207
+
208
+ def trimTrailZeros()
209
+ i = @digits.length
210
+ while i>0 && dig_value(i-1)==0
211
+ i -= 1
212
+ end
213
+ if @rep_pos>=i
214
+ @digits = @digits[0...i]
215
+ @rep_pos = i
216
+ end
217
+
218
+ if @digits==''
219
+ @digits = dig_char(0) # '0'
220
+ @rep_pos = 1
221
+ @dec_pos = 1
222
+ end
223
+
224
+ end
225
+
226
+ def trimLeadZeros()
227
+ i = 0
228
+ while i<@digits.length && dig_value(i)==0
229
+ i += 1
230
+ end
231
+ @digits = @digits[i...@digits.length]
232
+ @dec_pos -= i
233
+ @rep_pos -= i
234
+
235
+ if @digits==''
236
+ @digits = dig_char(0) # '0'
237
+ @rep_pos = 1
238
+ @dec_pos = 1
239
+ end
240
+
241
+ end
242
+
243
+ def trimZeros()
244
+ trimLeadZeros
245
+ trimTrailZeros
246
+ end
247
+
248
+ protected
249
+
250
+ def dig_value(i)
251
+ v = 0
252
+ if i>=@rep_pos
253
+ i -= @digits.length
254
+ i %= @digits.length - @rep_pos if @rep_pos<@digits.length
255
+ i += @rep_pos
256
+ end
257
+ if i>=0 && i<@digits.length
258
+ v = @dgs.digit_value(@digits[i]) #digcode_value(@digits[i])
259
+ end
260
+ return v>=0 && v<@base ? v : nil
261
+ end
262
+ #def digcode_value(c)
263
+ # v = c-?0
264
+ # if v>9
265
+ # v = 10 + c.chr.downcase[0] - ?a
266
+ # end
267
+ # v
268
+ # @dgs.digit_value(c)
269
+ #end
270
+
271
+ def dig_char(v)
272
+ c = ''
273
+ if v!=nil && v>=0 && v<@base
274
+ c = @dgs.digit_char(v).chr
275
+ end
276
+ c
277
+ end
278
+
279
+ end
280
+
281
+ class NeutralNum
282
+ public
283
+ def to_RepDec
284
+ n = RepDec.new(@base)
285
+ if special?
286
+
287
+ case special
288
+ when :nan
289
+ n.ip = :indeterminate
290
+ when :inf
291
+ if sign=='-'
292
+ n.ip = :posinfinity
293
+ else
294
+ n.ip :neginfinity
295
+ end
296
+ else
297
+ n = nil
298
+ end
299
+
300
+ else
301
+ if dec_pos<=0
302
+ n.ip = 0
303
+ n.d = text_to_digits(dig_char(0)*(-dec_pos) + digits)
304
+ elsif dec_pos >= digits.length
305
+ n.ip = digits.to_i(@base)
306
+ if rep_pos<dec_pos
307
+ i=0
308
+ (dec_pos-digits.length).times do
309
+ n.ip *= @base
310
+ n.ip += @dgs.digit_value(digits[rep_pos+i]) if rep_pos+i<digits.length
311
+ i += 1
312
+ i=0 if i>=digits.length-rep_pos
313
+ end
314
+ n.d = []
315
+ while i<digits.length-rep_pos
316
+ n.d << @dgs.digit_value(digits[rep_pos+i])
317
+ i += 1
318
+ end
319
+ new_rep_pos = n.d.size + dec_pos
320
+ n.d += text_to_digits(digits[rep_pos..-1])
321
+ self.rep_pos = new_rep_pos
322
+ else
323
+ n.ip *= @base**(dec_pos-digits.length)
324
+ n.d = []
325
+ end
326
+ else
327
+ n.ip = digits[0...dec_pos].to_i(@base)
328
+ n.d = text_to_digits(digits[dec_pos..-1])
329
+ if rep_pos<dec_pos
330
+ new_rep_pos = n.d.size + dec_pos
331
+ n.d += text_to_digits(digits[rep_pos..-1])
332
+ self.rep_pos = new_rep_pos
333
+ puts "--rep_pos=#{rep_pos}"
334
+ end
335
+ end
336
+ n.sign = -1 if sign=='-'
337
+ n.rep_i = rep_pos - dec_pos
338
+ end
339
+ n.normalize!(!inexact) # keep trailing zeros for inexact numbers
340
+ return n
341
+ end
342
+ protected
343
+ def text_to_digits(txt)
344
+ #txt.split('').collect{|c| @dgs.digit_value(c)}
345
+ ds = []
346
+ txt.each_byte{|b| ds << @dgs.digit_value(b)}
347
+ ds
348
+ end
349
+ end
350
+
351
+ class RepDec
352
+ public
353
+ def to_NeutralNum(base_dgs=nil)
354
+ num = NeutralNum.new
355
+ if !ip.is_a?(Integer)
356
+
357
+ case ip
358
+ when :indeterminate
359
+ num.set_special :nan
360
+ when :posinfinity
361
+ num.set_special :inf,'+'
362
+ when :neginfinity
363
+ num.set_special :inf,'-'
364
+ else
365
+ num = nil
366
+ end
367
+
368
+ else
369
+ base_dgs ||= DigitsDef.base(@radix)
370
+ # assert base_dgs.radix == @radix
371
+ signch = sign<0 ? '-' : '+'
372
+ decimals = ip.to_s(@radix)
373
+ dec_pos = decimals.length
374
+ d.each {|dig| decimals << base_dgs.digit_char(dig) }
375
+ rep_pos = rep_i==nil ? decimals.length : dec_pos + rep_i
376
+ num.set signch, decimals, dec_pos, rep_pos, base_dgs
377
+ end
378
+ return num
379
+ end
380
+ end
381
+
382
+ # A Fmt object defines a numeric format.
383
+ #
384
+ # The formatting aspects managed by Fmt are:
385
+ # * mode and precision
386
+ # - #mode() and #orec() set the main paramters
387
+ # - see also #show_all_digits(), #approx_mode(), #insignificant_digits(),
388
+ # #sci_digits(), #show_exp_plus() and #show_plus()
389
+ # * separators
390
+ # - see #sep() and #grouping()
391
+ # * field justfification
392
+ # - #width() and the shortcut #pad0s()
393
+ # * numerical base
394
+ # - #base()
395
+ # * repeating numerals
396
+ # - #rep()
397
+ #
398
+ # Note that for every aspect there are also corresponding _mutator_
399
+ # methos (its name ending with a bang) that modify an object in place,
400
+ # instead of returning an altered copy.
401
+ #
402
+ # This class also contains class methods for numeric conversion:
403
+ # * Fmt.convert
404
+ # and for default and other predefined formats:
405
+ # * Fmt.default / Fmt.default=
406
+ # * Fmt.[] / Fmt.[]=
407
+ #
408
+ # The actual formatted reading and writting if performed by
409
+ # * #nio_write() (Nio::Formattable#nio_write)
410
+ # * #nio_read() (Nio::Formattable::ClassMethods#nio_read)
411
+ # Finally numerical objects can be rounded according to a format:
412
+ # * #nio_round() (Nio::Formattable#nio_round)
413
+ class Fmt
414
+ include StateEquivalent
415
+
416
+ class Error < StandardError # :nodoc:
417
+ end
418
+ class InvalidOption < Error # :nodoc:
419
+ end
420
+ class InvalidFormat < Error # :nodoc:
421
+ end
422
+
423
+ @@default_rounding_mode = :even
424
+ def initialize()
425
+
426
+ @dec_sep = '.'
427
+ @grp_sep = ','
428
+ @grp = []
429
+
430
+ @ndig = :exact
431
+ @mode=:gen
432
+ @round=Fmt.default_rounding_mode
433
+ @all_digits = false
434
+ @approx = :only_sig
435
+ @non_sig = '' # marker for insignificant digits of inexact values e.g. '#','0'
436
+ @sci_format = 1 # number of integral digits in the mantissa: -1 for all
437
+
438
+ @show_plus = false
439
+ @show_exp_plus = false
440
+
441
+ @plus_symbol = nil
442
+ @minus_symbol = nil
443
+
444
+ @rep_begin = '<'
445
+ @rep_end = '>'
446
+ @rep_auto = '...'
447
+ @rep_n = 2
448
+ @rep_in = true
449
+
450
+ @width = 0
451
+ @fill_char = ' '
452
+ @adjust=:right
453
+
454
+ @base_radix = 10
455
+ @base_uppercase = true
456
+ @base_digits = DigitsDef.base(@base_radix, !@base_uppercase)
457
+ @show_base = false
458
+ @base_indicators = { 2=>'b', 8=>'o', 10=>'', 16=>'h', 0=>'r'} # 0: generic (used with radix)
459
+ @base_prefix = false
460
+
461
+ @nan_txt = 'NAN'
462
+ @inf_txt = 'Infinity'
463
+
464
+ yield self if block_given?
465
+ end
466
+
467
+ # Defines the separators used in numerals. This is relevant to
468
+ # both input and output.
469
+ #
470
+ # The first argument is the radix point separator (usually
471
+ # a point or a comma; by default it is a point.)
472
+ #
473
+ # The second argument is the group separator.
474
+ #
475
+ # Finally, the third argument is an array that defines the groups
476
+ # of digits to separate.
477
+ # By default it's [], which means that no grouping will be produced on output
478
+ # (but the group separator defined will be ignored in input.)
479
+ # To produce the common thousands separation a value of [3] must be passed,
480
+ # which means that groups of 3 digits are used.
481
+ def sep(dec_sep,grp_sep=nil,grp=nil)
482
+ dup.sep!(dec_sep,grp_sep,grp)
483
+ end
484
+ # This is the mutator version of #sep().
485
+ def sep!(dec_sep,grp_sep=nil,grp=nil)
486
+ set! :dec_sep=>dec_sep, :grp_sep=>grp_sep, :grp=>grp
487
+ end
488
+
489
+ # This defines the grouping of digits (which can also be defined in #sep()
490
+ def grouping(grp=[3],grp_sep=nil)
491
+ dup.grouping!(grp,grp_sep)
492
+ end
493
+ # This is the mutator version of #grouping().
494
+ def grouping!(grp=[3],grp_sep=nil)
495
+ set! :grp_sep=>grp_sep, :grp=>grp
496
+ end
497
+
498
+ # This is a shortcut to return a new default Fmt object
499
+ # and define the separators as with #sep().
500
+ def Fmt.sep(dec_sep,grp_sep=nil,grp=nil)
501
+ Fmt.default.sep(dec_sep,grp_sep,grp)
502
+ end
503
+ # This is a shortcut to return a new default Fmt object
504
+ # and define the grouping as with #grouping().
505
+ def Fmt.grouping(grp=[3],grp_sep=nil)
506
+ Fmt.default.grouping(grp,grp_sep)
507
+ end
508
+
509
+ # Define the formatting mode. There are two fixed parameters:
510
+ # - <tt>mode</tt> (only relevant for output)
511
+ # [<tt>:gen</tt>]
512
+ # (general) chooses automatically the shortes format
513
+ # [<tt>:fix</tt>]
514
+ # (fixed precision) is a simple format with a fixed number of digits
515
+ # after the point
516
+ # [<tt>:sig</tt>]
517
+ # (significance precision) is like :fix but using significant digits
518
+ # [<tt>:sci</tt>]
519
+ # (scientific) is the exponential form 1.234E2
520
+ # - <tt>precision</tt> (number of digits or :exact, only used for output)
521
+ # [<tt>exact</tt>]
522
+ # means that as many digits as necessary to unambiguosly define the
523
+ # value are used; this is the default.
524
+ #
525
+ # Other paramters can be passed in a hash after <tt>precision</tt>
526
+ # - <tt>:round</tt> rounding mode applied to conversions
527
+ # (this is relevant for both input and output). It must be one of:
528
+ # [<tt>:inf</tt>]
529
+ # rounds to nearest with ties toward infinite;
530
+ # 1.5 is rounded to 2, -1.5 to -2
531
+ # [<tt>:zero</tt>]
532
+ # rounds to nearest with ties toward zero;
533
+ # 1.5 is rounded to 1, -1.5 to 2
534
+ # [<tt>:even</tt>]
535
+ # rounds to the nearest with ties toward an even digit;
536
+ # 1.5 rounds to 2, 2.5 to 2
537
+ # - <tt>:approx</tt> approximate mode
538
+ # [<tt>:only_sig</tt>]
539
+ # (the default) treats the value as an approximation and only
540
+ # significant digits (those that cannot take an arbitrary value without
541
+ # changing the specified value) are shown.
542
+ # [<tt>:exact</tt>]
543
+ # the value is interpreted as exact, there's no distinction between
544
+ # significant and insignificant digits.
545
+ # [<tt>:simplify</tt>]
546
+ # the value is simplified, if possible to a simpler (rational) value.
547
+ # - <tt>:show_all_digits</tt> if true, this forces to show digits that
548
+ # would otherwise not be shown in the <tt>:gen</tt> format: trailing
549
+ # zeros of exact types or non-signficative digits of inexact types.
550
+ # - <tt>:nonsignficative_digits</tt> assigns a character to display
551
+ # insignificant digits, # by default
552
+ def mode(mode,precision=nil,options={})
553
+ dup.mode!(mode,precision,options)
554
+ end
555
+ # This is the mutator version of #mode().
556
+ def mode!(mode,precision=nil,options={})
557
+ set! options.merge(:mode=>mode, :ndig=>precision)
558
+ end
559
+
560
+ # Defines the formatting mode like #mode() but using a different
561
+ # order of the first two parameters parameters, which is useful
562
+ # to change the precision only. Refer to #mode().
563
+ def prec(precision,mode=nil, options={})
564
+ dup.prec! precision, mode, options
565
+ end
566
+ # This is the mutator version of #prec().
567
+ def prec!(precision,mode=:gen, options={})
568
+ set! options.merge(:mode=>mode, :ndig=>precision)
569
+ end
570
+
571
+ # This is a shortcut to return a new default Fmt object
572
+ # and define the formatting mode as with #mode()
573
+ def Fmt.mode(mode,ndig=nil,options={})
574
+ Fmt.default.mode(mode,ndig,options)
575
+ end
576
+ # This is a shortcut to return a new default Fmt object
577
+ # and define the formatting mode as with #prec()
578
+ def Fmt.prec(ndig,mode=nil,options={})
579
+ Fmt.default.prec(ndig,mode,options)
580
+ end
581
+
582
+ # Rounding mode used when not specified otherwise
583
+ def Fmt.default_rounding_mode
584
+ @@default_rounding_mode
585
+ end
586
+ # The default rounding can be changed here; it starts with the value :even.
587
+ # See the rounding modes available in the description of method #mode().
588
+ def Fmt.default_rounding_mode=(m)
589
+ @@default_rounding_mode=m
590
+ Fmt.default = Fmt.default.round(m)
591
+ end
592
+
593
+ # This controls the display of the digits that are not necessary
594
+ # to specify the value unambiguosly (e.g. trailing zeros).
595
+ #
596
+ # The true (default) value forces the display of the requested number of digits
597
+ # and false will display only necessary digits.
598
+ def show_all_digits(ad=true)
599
+ dup.show_all_digits! ad
600
+ end
601
+ # This is the mutator version of #show_all_digits().
602
+ def show_all_digits!(ad=true)
603
+ set! :all_digits=>ad
604
+ end
605
+ # This defines the approximate mode (:only_sig, :exact, :simplify)
606
+ # just like the last parameter of #mode()
607
+ def approx_mode(mode)
608
+ dup.approx_mode! mode
609
+ end
610
+ # This is the mutator version of #approx_mode().
611
+ def approx_mode!(mode)
612
+ set! :approx=>mode
613
+ end
614
+ # Defines a character to stand for insignificant digits when
615
+ # a specific number of digits has been requested greater than then
616
+ # number of significant digits (for approximate types).
617
+ def insignificant_digits(ch='#')
618
+ dup.insignificant_digits! ch
619
+ end
620
+ # This is the mutator version of #insignificant_digits().
621
+ def insignificant_digits!(ch='#')
622
+ ch ||= ''
623
+ set! :non_sig=>ch
624
+ end
625
+ # Defines the number of significan digits before the radix separator
626
+ # in scientific notation. A negative value will set all significant digits
627
+ # before the radix separator. The special value <tt>:eng</tt> activates
628
+ # _engineering_ mode, in which the exponents are multiples of 3.
629
+ #
630
+ # For example:
631
+ # 0.1234.nio_write(Fmt.mode(:sci,4).sci_digits(0) -> 0.1234E0
632
+ # 0.1234.nio_write(Fmt.mode(:sci,4).sci_digits(3) -> 123.4E-3
633
+ # 0.1234.nio_write(Fmt.mode(:sci,4).sci_digits(-1) -> 1234.E-4
634
+ # 0.1234.nio_write(Fmt.mode(:sci,4).sci_digits(:eng) -> 123.4E-3
635
+ def sci_digits(n=-1)
636
+ dup.sci_digits! n
637
+ end
638
+ # This is the mutator version of #sci_digits().
639
+ def sci_digits!(n=-1)
640
+ set! :sci_format=>n
641
+ end
642
+
643
+ # This is a shortcut to return a new default Fmt object
644
+ # and define show_all_digits
645
+ def Fmt.show_all_digits(v=true)
646
+ Fmt.default.show_all_digits(v)
647
+ end
648
+ # This is a shortcut to return a new default Fmt object
649
+ # and define approx_mode
650
+ def Fmt.approx_mode(v)
651
+ Fmt.default.approx_mode(v)
652
+ end
653
+ # This is a shortcut to return a new default Fmt object
654
+ # and define insignificant digits
655
+ def Fmt.insignificant_digits(v='#')
656
+ Fmt.default.insignificant_digits(v)
657
+ end
658
+ # This is a shortcut to return a new default Fmt object
659
+ # and define sci_digits
660
+ def Fmt.sci_digits(v=-1)
661
+ Fmt.default.sci_digits(v)
662
+ end
663
+
664
+ # Controls the display of the sign for positive numbers
665
+ def show_plus(sp=true)
666
+ dup.show_plus! sp
667
+ end
668
+ # This is the mutator version of #show_plus().
669
+ def show_plus!(sp=true)
670
+ set! :show_plus=>sp
671
+ set! :plus_symbol=>sp if sp.kind_of?(String)
672
+ self
673
+ end
674
+
675
+ # Controls the display of the sign for positive exponents
676
+ def show_exp_plus(sp=true)
677
+ dup.show_exp_plus! sp
678
+ end
679
+ # This is the mutator version of #show_plus().
680
+ def show_exp_plus!(sp=true)
681
+ set! :show_exp_plus=>sp
682
+ set! :plus_symbol=>sp if sp.kind_of?(String)
683
+ self
684
+ end
685
+
686
+ # This is a shortcut to return a new default Fmt object
687
+ # and define show_plus
688
+ def Fmt.show_plus(v=true)
689
+ Fmt.default.show_plus(v)
690
+ end
691
+ # This is a shortcut to return a new default Fmt object
692
+ # and define show_exp_plus
693
+ def Fmt.show_exp_plus(v=true)
694
+ Fmt.default.show_exp_plus(v)
695
+ end
696
+
697
+ # Defines the handling and notation for repeating numerals. The parameters
698
+ # can be passed in order or in a hash:
699
+ # [<tt>:begin</tt>] is the beginning delimiter of repeating section (<)
700
+ # [<tt>:end</tt>] is the ending delimiter of repeating section (<)
701
+ # [<tt>:suffix</tt>] is the suffix used to indicate a implicit repeating decimal
702
+ # [<tt>:rep</tt>]
703
+ # if this parameter is greater than zero, on output the repeating section
704
+ # is repeated the indicated number of times followed by the suffix;
705
+ # otherwise the delimited notation is used.
706
+ # [<tt>:read</tt>]
707
+ # (true/false) determines if repeating decimals are
708
+ # recognized on input (true)
709
+ def rep(*params)
710
+ dup.rep!(*params)
711
+ end
712
+ # This is the mutator version of #rep().
713
+ def rep!(*params)
714
+
715
+ params << {} if params.size==0
716
+ if params[0].kind_of?(Hash)
717
+ params = params[0]
718
+ else
719
+ begch,endch,autoch,rep,read = *params
720
+ params = {:begin=>begch,:end=>endch,:suffix=>autoch,:nreps=>rep,:read=>read}
721
+ end
722
+
723
+ set! params
724
+ end
725
+
726
+ # This is a shortcut to return a new default Fmt object
727
+ # and define the repeating decimals mode as with #rep()
728
+ def Fmt.rep(*params)
729
+ Fmt.default.rep(*params)
730
+ end
731
+
732
+ # Sets the justificaton width, mode and fill character
733
+ #
734
+ # The mode accepts these values:
735
+ # [<tt>:right</tt>] (the default) justifies to the right (adds padding at the left)
736
+ # [<tt>:left</tt>] justifies to the left (adds padding to the right)
737
+ # [<tt>:internal</tt>] like :right, but the sign is kept to the left, outside the padding.
738
+ # [<tt>:center</tt>] centers the number in the field
739
+ def width(w,adj=nil,ch=nil)
740
+ dup.width! w,adj,ch
741
+ end
742
+ # This is the mutator version of #width().
743
+ def width!(w,adj=nil,ch=nil)
744
+ set! :width=>w, :adjust=>adj, :fill_char=>ch
745
+ end
746
+ # Defines the justification (as #width()) with the given
747
+ # width, internal mode and filling with zeros.
748
+ #
749
+ # Note that if you also use grouping separators, the filling 0s
750
+ # will not be separated.
751
+ def pad0s(w)
752
+ dup.pad0s! w
753
+ end
754
+ # This is the mutator version of #pad0s().
755
+ def pad0s!(w)
756
+ width! w, :internal, '0'
757
+ end
758
+ # This is a shortcut to create a new Fmt object and define the width
759
+ # parameters as with #widht()
760
+ def Fmt.width(w,adj=nil,ch=nil)
761
+ Fmt.default.width(w,adj,ch)
762
+ end
763
+ # This is a shortcut to create a new Fmt object and define numeric
764
+ # padding as with #pad0s()
765
+ def Fmt.pad0s(w)
766
+ Fmt.default.pad0s(w)
767
+ end
768
+
769
+ # defines the numerical base; the second parameters forces the use
770
+ # of uppercase letters for bases greater than 10.
771
+ def base(b, uppercase=nil)
772
+ dup.base! b, uppercase
773
+ end
774
+ # This is the mutator version of #base().
775
+ def base!(b, uppercase=nil)
776
+ set! :base_radix=>b, :base_uppercase=>uppercase
777
+ end
778
+ # This is a shortcut to create a new Fmt object and define the base
779
+ def Fmt.base(b, uppercase=nil)
780
+ Fmt.default.base(b, uppercase)
781
+ end
782
+ # returns the exponent char used with the specified base
783
+ def get_exp_char(base) # :nodoc:
784
+ base ||= @base_radix
785
+ base<=10 ? 'E' : '^'
786
+ end
787
+
788
+ # returns the base
789
+ def get_base # :nodoc:
790
+ @base_radix
791
+ end
792
+ # returns the digit characters used for a base
793
+ def get_base_digits(b=nil) # :nodoc:
794
+ (b.nil? || b==@base_radix) ? @base_digits : DigitsDef.base(b,!@base_uppercase)
795
+ end
796
+ # returns true if uppercase digits are used
797
+ def get_base_uppercase? # :nodoc:
798
+ @base_uppercase
799
+ end
800
+
801
+ # returns the formatting mode
802
+ def get_mode # :nodoc:
803
+ @mode
804
+ end
805
+ # returns the precision (number of digits)
806
+ def get_ndig # :nodoc:
807
+ @ndig
808
+ end
809
+ # return the show_all_digits state
810
+ def get_all_digits? # :nodoc:
811
+ @all_digits
812
+ end
813
+ # returns the approximate mode
814
+ def get_approx # :nodoc:
815
+ @approx
816
+ end
817
+
818
+ # returns the rounding mode
819
+ def get_round # :nodoc:
820
+ @round
821
+ end
822
+
823
+ # Method used internally to format a neutral numeral
824
+ def nio_write_formatted(neutral) # :nodoc:
825
+ str = ''
826
+ if neutral.special?
827
+ str << neutral.sign
828
+ case neutral.special
829
+ when :inf
830
+ str << @inf_txt
831
+ when :nan
832
+ str << @nan_txt
833
+ end
834
+ else
835
+ zero = get_base_digits(neutral.base).digit_char(0).chr
836
+ neutral = neutral.dup
837
+ round! neutral
838
+ if neutral.zero?
839
+ str << neutral.sign if neutral.sign=='-' # show - if number was <0 before rounding
840
+ str << zero
841
+ if @ndig.kind_of?(Numeric) && @ndig>0 && @mode==:fix
842
+ str << @dec_sep << zero*@ndig
843
+ end
844
+ else
845
+
846
+ neutral.trimLeadZeros
847
+ actual_mode = @mode
848
+ trim_trail_zeros = !@all_digits # false
849
+
850
+ integral_digits = @sci_format
851
+ if integral_digits == :eng
852
+ integral_digits = 1
853
+ while (neutral.dec_pos - integral_digits).modulo(3) != 0
854
+ integral_digits += 1
855
+ end
856
+ elsif integral_digits==:all || integral_digits < 0
857
+ if neutral.inexact? && @non_sig!='' && @ndig.kind_of?(Numeric)
858
+ integral_digits = @ndig
859
+ else
860
+ integral_digits = neutral.digits.length
861
+ end
862
+ end
863
+ exp = neutral.dec_pos - integral_digits
864
+
865
+ case actual_mode
866
+ when :gen # general (automatic)
867
+ # @ndig means significant digits
868
+ actual_mode = :sig
869
+ actual_mode = :sci if use_scientific?(neutral, exp)
870
+ trim_trail_zeros = !@all_digits # true
871
+ end
872
+
873
+ case actual_mode
874
+ when :fix, :sig #, :gen
875
+
876
+
877
+ if @show_plus || neutral.sign!='+'
878
+ str << ({'-'=>@minus_symbol, '+'=>@plus_symbol}[neutral.sign] || neutral.sign)
879
+ end
880
+
881
+
882
+
883
+ if @show_base && @base_prefix
884
+ b_prefix = @base_indicators[neutral.base]
885
+ str << b_prefix if b_prefix
886
+ end
887
+
888
+ if @ndig==:exact
889
+ neutral.sign = '+'
890
+ str << neutral.to_RepDec.getS(@rep_n, getRepDecOpt(neutral.base))
891
+ else
892
+ #zero = get_base_digits.digit_char(0).chr
893
+ ns_digits = ''
894
+
895
+ nd = neutral.digits.length
896
+ if actual_mode==:fix
897
+ nd -= neutral.dec_pos
898
+ end
899
+ if neutral.inexact? && @ndig>nd # assert no rep-dec.
900
+ ns_digits = @non_sig*(@ndig-nd)
901
+ end
902
+
903
+ digits = neutral.digits + ns_digits
904
+ if neutral.dec_pos<=0
905
+ str << zero+@dec_sep+zero*(-neutral.dec_pos) + digits
906
+ elsif neutral.dec_pos >= digits.length
907
+ str << group(digits + zero*(neutral.dec_pos-digits.length))
908
+ else
909
+ str << group(digits[0...neutral.dec_pos]) + @dec_sep + digits[neutral.dec_pos..-1]
910
+ end
911
+ end
912
+
913
+ #str = str.chomp(zero).chomp(@dec_sep) if trim_trail_zeros && str.include?(@dec_sep)
914
+ if trim_trail_zeros && str.include?(@dec_sep) && str[-@rep_auto.size..-1]!=@rep_auto
915
+ str.chop! while str[-1]==zero[0]
916
+ str.chomp!(@dec_sep)
917
+ #puts str
918
+ end
919
+
920
+
921
+ when :sci
922
+
923
+
924
+ if @show_plus || neutral.sign!='+'
925
+ str << ({'-'=>@minus_symbol, '+'=>@plus_symbol}[neutral.sign] || neutral.sign)
926
+ end
927
+
928
+
929
+ if @show_base && @base_prefix
930
+ b_prefix = @base_indicators[neutral.base]
931
+ str << b_prefix if b_prefix
932
+ end
933
+
934
+ #zero = get_base_digits.digit_char(0).chr
935
+ if @ndig==:exact
936
+ neutral.sign = '+'
937
+ neutral.dec_pos-=exp
938
+ str << neutral.to_RepDec.getS(@rep_n, getRepDecOpt(neutral.base))
939
+ else
940
+ ns_digits = ''
941
+
942
+ nd = neutral.digits.length
943
+ if actual_mode==:fix
944
+ nd -= neutral.dec_pos
945
+ end
946
+ if neutral.inexact? && @ndig>nd # assert no rep-dec.
947
+ ns_digits = @non_sig*(@ndig-nd)
948
+ end
949
+
950
+ digits = neutral.digits + ns_digits
951
+ str << ((integral_digits<1) ? zero : digits[0...integral_digits])
952
+ str << @dec_sep
953
+ str << digits[integral_digits...@ndig]
954
+ pad_right =(@ndig+1-str.length)
955
+ str << zero*pad_right if pad_right>0 && !neutral.inexact? # maybe we didn't have enought digits
956
+ end
957
+
958
+ #str = str.chomp(zero).chomp(@dec_sep) if trim_trail_zeros && str.include?(@dec_sep)
959
+ if trim_trail_zeros && str.include?(@dec_sep) && str[-@rep_auto.size..-1]!=@rep_auto
960
+ str.chop! while str[-1]==zero[0]
961
+ str.chomp!(@dec_sep)
962
+ #puts str
963
+ end
964
+
965
+ str << get_exp_char(neutral.base)
966
+ if @show_exp_plus || exp<0
967
+ str << (exp<0 ? (@minus_symbol || '-') : (@plus_symbol || '+'))
968
+ end
969
+ str << exp.abs.to_s
970
+
971
+ end
972
+
973
+ end
974
+ end
975
+
976
+ if @show_base && !@base_prefix
977
+ b_prefix = @base_indicators[neutral.base]
978
+ str << b_prefix if b_prefix
979
+ end
980
+
981
+
982
+ if @width>0 && @fill_char!=''
983
+ l = @width - str.length
984
+ if l>0
985
+ case @adjust
986
+ when :internal
987
+ sign = ''
988
+ if str[0,1]=='+' || str[0,1]=='-'
989
+ sign = str[0,1]
990
+ str = str[1...str.length]
991
+ end
992
+ str = sign + @fill_char*l + str
993
+ when :center
994
+ str = @fill_char*(l/2) + str + @fill_char*(l-l/2)
995
+ when :right
996
+ str = @fill_char*l + str
997
+ when :left
998
+ str = str + @fill_char*l
999
+ end
1000
+ end
1001
+ end
1002
+
1003
+ return str
1004
+ end
1005
+
1006
+ # round a neutral numeral according to the format options
1007
+ def round!(neutral) # :nodoc:
1008
+ neutral.round! @ndig, @mode, @round
1009
+ end
1010
+
1011
+ @@sci_fmt = nil
1012
+
1013
+ def nio_read_formatted(txt) # :nodoc:
1014
+ txt = txt.dup
1015
+ num = nil
1016
+
1017
+ base = nil
1018
+
1019
+ base ||= get_base
1020
+
1021
+ zero = get_base_digits(base).digit_char(0).chr
1022
+ txt.tr!(@non_sig,zero) # we don't simply remove it because it may be before the radix point
1023
+
1024
+ exp = 0
1025
+ x_char = get_exp_char(base)
1026
+
1027
+ exp_i = txt.index(x_char)
1028
+ exp_i = txt.index(x_char.downcase) if exp_i===nil
1029
+ if exp_i!=nil
1030
+ exp = txt[exp_i+1...txt.length].to_i
1031
+ txt = txt[0...exp_i]
1032
+ end
1033
+
1034
+
1035
+ opt = getRepDecOpt(base)
1036
+ if @rep_in
1037
+ #raise InvalidFormat,"Invalid numerical base" if base!=10
1038
+ rd = RepDec.new # get_base not necessary: setS sets it from options
1039
+ rd.setS txt, opt
1040
+ num = rd.to_NeutralNum(opt.digits)
1041
+ else
1042
+ # to do: use RepDec.parse; then build NeutralNum directly
1043
+ opt.set_delim '',''
1044
+ opt.set_suffix ''
1045
+ rd = RepDec.new # get_base not necessary: setS sets it from options
1046
+ rd.setS txt, opt
1047
+ num = rd.to_NeutralNum(opt.digits)
1048
+ end
1049
+ num.rounding = get_round
1050
+ num.dec_pos += exp
1051
+ return num
1052
+ end
1053
+
1054
+
1055
+ @@fmts = {
1056
+ :def=>Fmt.new.freeze
1057
+ }
1058
+ # Returns the current default format.
1059
+ def self.default
1060
+ d = self[:def]
1061
+ if block_given?
1062
+ d = d.dup
1063
+ yield d
1064
+ end
1065
+ d
1066
+ end
1067
+ # Defines the current default format.
1068
+ def self.default=(fmt)
1069
+ self[:def] = fmt
1070
+ end
1071
+ # Assigns a format to a name in the formats repository.
1072
+ def self.[]=(tag,fmt_def)
1073
+ @@fmts[tag.to_sym]=fmt_def.freeze
1074
+ end
1075
+ # Retrieves a named format from the repository.
1076
+ def self.[](tag)
1077
+ @@fmts[tag.to_sym]
1078
+ end
1079
+
1080
+ protected
1081
+
1082
+ @@valid_properties = nil
1083
+ ALIAS_PROPERTIES = {
1084
+ :show_all_digits=>:all_digits,
1085
+ :rounding_mode=>:round,
1086
+ :approx_mode=>:approx,
1087
+ :sci_digits=>:sci_format,
1088
+ :non_signitificative_digits=>:non_sig,
1089
+ :begin=>:rep_begin,
1090
+ :end=>:rep_end,
1091
+ :suffix=>:rep_auto,
1092
+ :nreps=>:rep_n,
1093
+ :read=>:rep_in
1094
+ }
1095
+ def set!(properties={}) # :nodoc:
1096
+
1097
+
1098
+ @@valid_properties ||= instance_variables.collect{|v| v[1..-1].to_sym}
1099
+
1100
+
1101
+ properties.each do |k,v|
1102
+ al = ALIAS_PROPERTIES[k]
1103
+ if al
1104
+ properties[al] = v
1105
+ properties.delete k
1106
+ elsif !@@valid_properties.include?(k)
1107
+ raise InvalidOption, "Invalid option: #{k}"
1108
+ end
1109
+ end
1110
+
1111
+
1112
+ if properties[:grp_sep].nil? && !properties[:dec_sep].nil? && properties[:dec_sep]!=@dec_sep && properties[:dec_sep]==@grp_sep
1113
+ properties[:grp_sep] = properties[:dec_sep]=='.' ? ',' : '.'
1114
+ end
1115
+
1116
+ if properties[:all_digits].nil? && (properties[:ndig] || properties[:mode])
1117
+ ndig = properties[:ndig] || @ndig
1118
+ mode = properties[:mode] || @mode
1119
+ properties[:all_digits] = ndig!=:exact && mode!=:gen
1120
+ end
1121
+
1122
+ if !properties[:all_digits].nil? && properties[:non_sig].nil?
1123
+ properties[:non_sig] = '' unless properties[:all_digits]
1124
+ elsif !properties[:non_sig].nil? && properties[:all_digits].nil?
1125
+ properties[:all_digits] = true if properties[:non_sig]!=''
1126
+ end
1127
+
1128
+ if !properties[:base_radix].nil? || !properties[:base_uppercase].nil?
1129
+ base = properties[:base_radix] || @base_radix
1130
+ uppercase = properties[:base_uppercase] || @base_uppercase
1131
+ properties[:base_digits] = DigitsDef.base(base, !uppercase)
1132
+ end
1133
+
1134
+
1135
+ properties.each do |k,v|
1136
+ instance_variable_set "@#{k}", v unless v.nil?
1137
+ end
1138
+
1139
+ self
1140
+ end
1141
+
1142
+ def set(properties={}) # :nodoc:
1143
+ self.dup.set!(properties)
1144
+ end
1145
+
1146
+ def use_scientific?(neutral,exp) # :nodoc:
1147
+ nd = @ndig.kind_of?(Numeric) ? @ndig : [neutral.digits.length,10].max
1148
+ if @@sci_fmt==:hp
1149
+ puts " #{nd} ndpos=#{neutral.dec_pos} ndlen=#{neutral.digits.length}"
1150
+ neutral.dec_pos>nd || ([neutral.digits.length,nd].min-neutral.dec_pos)>nd
1151
+ else
1152
+ exp<-4 || exp>=nd
1153
+ end
1154
+ end
1155
+
1156
+ def getRepDecOpt(base=nil) # :nodoc:
1157
+ rd_opt = RepDec::Opt.new
1158
+ rd_opt.begin_rep = @rep_begin
1159
+ rd_opt.end_rep = @rep_end
1160
+ rd_opt.auto_rep = @rep_auto
1161
+ rd_opt.dec_sep = @dec_sep
1162
+ rd_opt.grp_sep = @grp_sep
1163
+ rd_opt.grp = @grp
1164
+ rd_opt.inf_txt = @inf_txt
1165
+ rd_opt.nan_txt = @nan_txt
1166
+ rd_opt.set_digits(get_base_digits(base))
1167
+ # if base && (base != get_base_digits.radix)
1168
+ # rd_opt.set_digits(get_base_digits(base))
1169
+ # else
1170
+ # rd_opt.set_digits get_base_digits
1171
+ # end
1172
+ return rd_opt
1173
+ end
1174
+
1175
+ def group(digits) # :nodoc:
1176
+ RepDec.group_digits(digits, getRepDecOpt)
1177
+ end
1178
+
1179
+ end
1180
+
1181
+ # This is a mix-in module to add formatting capabilities no numerical classes.
1182
+ # A class that includes this module should provide the methods
1183
+ # nio_write_neutral(fmt):: an instance method to write the value to
1184
+ # a neutral numeral. The format is passed so that
1185
+ # the base, for example, is available.
1186
+ # nio_read_neutral(neutral):: a class method to create a value from a neutral
1187
+ # numeral.
1188
+ module Formattable
1189
+
1190
+ # This is the method available in all formattable objects
1191
+ # to format the value into a text string according
1192
+ # to the optional format passed.
1193
+ def nio_write(fmt=Fmt.default)
1194
+ neutral = nio_write_neutral(fmt)
1195
+ fmt.nio_write_formatted(neutral)
1196
+ end
1197
+
1198
+ module ClassMethods
1199
+ # This is the method available in all formattable clases
1200
+ # to read a formatted value from a text string into
1201
+ # a value the class, according to the optional format passed.
1202
+ def nio_read(txt,fmt=Fmt.default)
1203
+ neutral = fmt.nio_read_formatted(txt)
1204
+ nio_read_neutral neutral
1205
+ end
1206
+ end
1207
+
1208
+ # Round a formattable object according to the rounding mode and
1209
+ # precision of a format.
1210
+ def nio_round(fmt=Fmt.default)
1211
+ neutral = nio_write_neutral(fmt)
1212
+ fmt.round! neutral
1213
+ self.class.nio_read_neutral neutral
1214
+ end
1215
+
1216
+ def self.append_features(mod) # :nodoc:
1217
+ super
1218
+ mod.extend ClassMethods
1219
+ end
1220
+
1221
+ end
1222
+
1223
+ Fmt[:comma] = Fmt.sep(',','.')
1224
+ Fmt[:comma_th] = Fmt.sep(',','.',[3])
1225
+ Fmt[:dot] = Fmt.sep('.',',')
1226
+ Fmt[:dot_th] = Fmt.sep('.',',',[3])
1227
+ Fmt[:code] = Fmt.new.prec(20) # don't use :exact to avoid repeating numerals
1228
+
1229
+ class Fmt
1230
+ # Intermediate conversion format for simplified conversion
1231
+ CONV_FMT = Fmt.prec(:exact).rep('<','>','...',0).approx_mode(:simplify)
1232
+ # Intermediate conversion format for exact conversion
1233
+ CONV_FMT_STRICT = Fmt.prec(:exact).rep('<','>','...',0).approx_mode(:exact)
1234
+ # Numerical conversion: converts the quantity +x+ to an object
1235
+ # of class +type+.
1236
+ #
1237
+ # The third parameter is the kind of conversion:
1238
+ # [<tt>:approx</tt>]
1239
+ # Tries to find an approximate simpler value if possible for inexact
1240
+ # numeric types. This is the default. This is slower in general and
1241
+ # may take some seconds in some cases.
1242
+ # [<tt>:exact</tt>]
1243
+ # Performs a conversion as exact as possible.
1244
+ # The third parameter is true for approximate
1245
+ # conversion (inexact values are simplified if possible) and false
1246
+ # for conversions as exact as possible.
1247
+ def Fmt.convert(x, type, mode=:approx)
1248
+ fmt = mode==:approx ? CONV_FMT : CONV_FMT_STRICT
1249
+ # return x.prec(type)
1250
+ if !(x.is_a?(type))
1251
+ # return type.nio_read(x.nio_write(fmt),fmt)
1252
+
1253
+ x = x.nio_write_neutral(fmt)
1254
+ x = type.nio_read_neutral(x)
1255
+
1256
+ end
1257
+ x
1258
+ end
1259
+ end
1260
+
1261
+ module_function
1262
+
1263
+ def nio_float_to_bigdecimal(x,prec) # :nodoc:
1264
+ if prec.nil?
1265
+ x = Fmt.convert(x,BigDecimal,:approx)
1266
+ elsif prec==:exact
1267
+ x = Fmt.convert(x,BigDecimal,:exact)
1268
+ else
1269
+ x = BigDecimal(x.nio_write(Nio::Fmt.new.prec(prec,:sig)))
1270
+ end
1271
+ x
1272
+ end
1273
+
1274
+
1275
+ module Clinger # :nodoc: all
1276
+ module_function
1277
+
1278
+ def algM(f,e,round_mode,eb=10,beta=Float::RADIX,n=Float::MANT_DIG,min_e=Float::MIN_EXP-Float::MANT_DIG,max_e=Float::MAX_EXP-Float::MANT_DIG)
1279
+
1280
+ if e<0
1281
+ u,v,k = f,eb**(-e),0
1282
+ else
1283
+ u,v,k = f*(eb**e),1,0
1284
+ end
1285
+
1286
+ loop do
1287
+ x = u.div(v)
1288
+ # overflow if k>=max_e
1289
+ if (x>=beta**(n-1) && x<beta**n) || k==min_e || k==max_e
1290
+ return ratio_float(u,v,k,round_mode,beta,n)
1291
+ elsif x<beta**(n-1)
1292
+ u *= beta
1293
+ k -= 1
1294
+ elsif x>=beta**n
1295
+ v *= beta
1296
+ k += 1
1297
+ end
1298
+ end
1299
+
1300
+ end
1301
+
1302
+ def ratio_float(u,v,k,round_mode,beta=Float::RADIX,n=Float::MANT_DIG)
1303
+ q,r = u.divmod(v)
1304
+ v_r = v-r
1305
+ z = Math.ldexp(q,k)
1306
+ if r<v_r
1307
+ z
1308
+ elsif r>v_r
1309
+ nextfloat z
1310
+ elsif (round_mode==:even && q.even?) || (round_mode==:zero)
1311
+ z
1312
+ else
1313
+ nextfloat z
1314
+ end
1315
+ end
1316
+
1317
+ # valid only for non-negative x
1318
+ def nextfloat(x)
1319
+ f,e = Math.frexp(x)
1320
+ e = Float::MIN_EXP if f==0
1321
+ e = [Float::MIN_EXP,e].max
1322
+ dx = Math.ldexp(1,e-Float::MANT_DIG) #Math.ldexp(Math.ldexp(1.0,-Float::MANT_DIG),e)
1323
+ if f==(1.0 - Math.ldexp(1,-Float::MANT_DIG))
1324
+ x + dx*2
1325
+ else
1326
+ x + dx
1327
+ end
1328
+ end
1329
+
1330
+ # valid only for non-negative x
1331
+ def prevfloat(x)
1332
+ f,e = Math.frexp(x)
1333
+ e = Float::MIN_EXP if f==0
1334
+ e = [Float::MIN_EXP,e].max
1335
+ dx = Math.ldexp(1,e-Float::MANT_DIG) #Math.ldexp(Math.ldexp(1.0,-Float::MANT_DIG),e)
1336
+ if e==Float::MIN_EXP || f!=0.5 #0.5==Math.ldexp(2**(bits-1),-Float::MANT_DIG)
1337
+ x - dx
1338
+ else
1339
+ x - dx/2 # x - Math.ldexp(Math.ldexp(1.0,-Float::MANT_DIG),e-1)
1340
+ end
1341
+ end
1342
+
1343
+ end
1344
+
1345
+ module BurgerDybvig # :nodoc: all
1346
+ module_function
1347
+
1348
+ def float_to_digits(v,f,e,round_mode,min_e,p,b,_B)
1349
+
1350
+ case round_mode
1351
+ when :even
1352
+ roundl = roundh = f.even?
1353
+ when :inf
1354
+ roundl = true
1355
+ roundh = false
1356
+ when :zero
1357
+ roundl = false
1358
+ roundh = true
1359
+ else
1360
+ # here we don't assume any rounding in the floating point numbers
1361
+ # the result is valid for any rounding but may produce more digits
1362
+ # than stricly necessary for specifica rounding modes.
1363
+ roundl = false
1364
+ roundh = false
1365
+ end
1366
+
1367
+ if e >= 0
1368
+ if f != exptt(b,p-1)
1369
+ be = exptt(b,e)
1370
+ r,s,m_p,m_m,k = scale(f*be*2,2,be,be,0,_B,roundl ,roundh,v)
1371
+ else
1372
+ be = exptt(b,e)
1373
+ be1 = be*b
1374
+ r,s,m_p,m_m,k = scale(f*be1*2,b*2,be1,be,0,_B,roundl ,roundh,v)
1375
+ end
1376
+ else
1377
+ if e==min_e or f != exptt(b,p-1)
1378
+ r,s,m_p,m_m,k = scale(f*2,exptt(b,-e)*2,1,1,0,_B,roundl ,roundh,v)
1379
+ else
1380
+ r,s,m_p,m_m,k = scale(f*b*2,exptt(b,1-e)*2,b,1,0,_B,roundl ,roundh,v)
1381
+ end
1382
+ end
1383
+ [k]+generate(r,s,m_p,m_m,_B,roundl ,roundh)
1384
+ end
1385
+
1386
+ def scale(r,s,m_p,m_m,k,_B,low_ok ,high_ok,v)
1387
+ return scale2(r,s,m_p,m_m,k,_B,low_ok ,high_ok) if v==0
1388
+ est = (logB(_B,v)-1E-10).ceil.to_i
1389
+ if est>=0
1390
+ fixup(r,s*exptt(_B,est),m_p,m_m,est,_B,low_ok,high_ok)
1391
+ else
1392
+ sc = exptt(_B,-est)
1393
+ fixup(r*sc,s,m_p*sc,m_m*sc,est,_B,low_ok,high_ok)
1394
+ end
1395
+ end
1396
+
1397
+ def fixup(r,s,m_p,m_m,k,_B,low_ok,high_ok)
1398
+ if (high_ok ? (r+m_p >= s) : (r+m_p > s)) # too low?
1399
+ [r,s*_B,m_p,m_m,k+1]
1400
+ else
1401
+ [r,s,m_p,m_m,k]
1402
+ end
1403
+ end
1404
+
1405
+ def scale2(r,s,m_p,m_m,k,_B,low_ok ,high_ok)
1406
+ loop do
1407
+ if (high_ok ? (r+m_p >= s) : (r+m_p > s)) # k is too low
1408
+ s *= _B
1409
+ k += 1
1410
+ elsif (high_ok ? ((r+m_p)*_B<s) : ((r+m_p)*_B<=s)) # k is too high
1411
+ r *= _B
1412
+ m_p *= _B
1413
+ m_m *= _B
1414
+ k -= 1
1415
+ else
1416
+ break
1417
+ end
1418
+ end
1419
+ [r,s,m_p,m_m,k]
1420
+ end
1421
+
1422
+ def generate(r,s,m_p,m_m,_B,low_ok ,high_ok)
1423
+ list = []
1424
+ loop do
1425
+ d,r = (r*_B).divmod(s)
1426
+ m_p *= _B
1427
+ m_m *= _B
1428
+ tc1 = low_ok ? (r<=m_m) : (r<m_m)
1429
+ tc2 = high_ok ? (r+m_p >= s) : (r+m_p > s)
1430
+
1431
+ if not tc1
1432
+ if not tc2
1433
+ list << d
1434
+ else
1435
+ list << d+1
1436
+ break
1437
+ end
1438
+ else
1439
+ if not tc2
1440
+ list << d
1441
+ break
1442
+ else
1443
+ if r*2 < s
1444
+ list << d
1445
+ break
1446
+ else
1447
+ list << d+1
1448
+ break
1449
+ end
1450
+ end
1451
+ end
1452
+
1453
+ end
1454
+ list
1455
+ end
1456
+
1457
+ $exptt_table = Array.new(326)
1458
+ (0...326).each{|i| $exptt_table[i]=10**i}
1459
+ def exptt(_B, k)
1460
+ if _B==10 && k>=0 && k<326
1461
+ $exptt_table[k]
1462
+ else
1463
+ _B**k
1464
+ end
1465
+ end
1466
+
1467
+ $logB_table = Array.new(37)
1468
+ (2...37).each{|b| $logB_table[b]=1.0/Math.log(b)}
1469
+ def logB(_B, x)
1470
+ if _B>=2 && _B<37
1471
+ Math.log(x)*$logB_table[_B]
1472
+ else
1473
+ Math.log(x)/Math.log(_B)
1474
+ end
1475
+ end
1476
+
1477
+ def float_to_digits_max(v,f,e,round_mode,min_e,p,b,_B)
1478
+
1479
+ case round_mode
1480
+ when :even
1481
+ roundl = roundh = f.even?
1482
+ when :inf
1483
+ roundl = true
1484
+ roundh = false
1485
+ when :zero
1486
+ roundl = false
1487
+ roundh = true
1488
+ else
1489
+ # here we don't assume any rounding in the floating point numbers
1490
+ # the result is valid for any rounding but may produce more digits
1491
+ # than stricly necessary for specifica rounding modes.
1492
+ roundl = false
1493
+ roundh = false
1494
+ end
1495
+
1496
+ if e >= 0
1497
+ if f != exptt(b,p-1)
1498
+ be = exptt(b,e)
1499
+ r,s,m_p,m_m,k = scale(f*be*2,2,be,be,0,_B,roundl ,roundh,v)
1500
+ else
1501
+ be = exptt(b,e)
1502
+ be1 = be*b
1503
+ r,s,m_p,m_m,k = scale(f*be1*2,b*2,be1,be,0,_B,roundl ,roundh,v)
1504
+ end
1505
+ else
1506
+ if e==min_e or f != exptt(b,p-1)
1507
+ r,s,m_p,m_m,k = scale(f*2,exptt(b,-e)*2,1,1,0,_B,roundl ,roundh,v)
1508
+ else
1509
+ r,s,m_p,m_m,k = scale(f*b*2,exptt(b,1-e)*2,b,1,0,_B,roundl ,roundh,v)
1510
+ end
1511
+ end
1512
+ [k]+generate_max(r,s,m_p,m_m,_B,roundl ,roundh)
1513
+ end
1514
+
1515
+ def generate_max(r,s,m_p,m_m,_B,low_ok ,high_ok)
1516
+ list = [false]
1517
+ loop do
1518
+ d,r = (r*_B).divmod(s)
1519
+ m_p *= _B
1520
+ m_m *= _B
1521
+
1522
+ list << d
1523
+
1524
+ tc1 = low_ok ? (r<=m_m) : (r<m_m)
1525
+ tc2 = high_ok ? (r+m_p >= s) : (r+m_p > s)
1526
+
1527
+ if tc1 && tc2
1528
+ list[0] = true if r*2 >= s
1529
+ break
1530
+ end
1531
+ end
1532
+ list
1533
+ end
1534
+
1535
+ end
1536
+
1537
+ end
1538
+
1539
+ class Float
1540
+ include Nio::Formattable
1541
+ def self.nio_read_neutral(neutral)
1542
+ x = nil
1543
+
1544
+ honor_rounding = true
1545
+
1546
+ if neutral.special?
1547
+ case neutral.special
1548
+ when :nan
1549
+ x = 0.0/0.0
1550
+ when :inf
1551
+ x = (neutral.sign=='-' ? -1.0 : +1.0)/0.0
1552
+ end
1553
+ elsif neutral.rep_pos<neutral.digits.length
1554
+
1555
+ x,y = neutral.to_RepDec.getQ
1556
+ x = Float(x)/y
1557
+
1558
+ else
1559
+ nd = neutral.base==10 ? Float::DIG : ((Float::MANT_DIG-1)*Math.log(2)/Math.log(neutral.base)).floor
1560
+ k = neutral.dec_pos-neutral.digits.length
1561
+ if !honor_rounding && (neutral.digits.length<=nd && k.abs<=15)
1562
+ x = neutral.digits.to_i(neutral.base).to_f
1563
+ if k<0
1564
+ x /= Float(neutral.base**-k)
1565
+ else
1566
+ x *= Float(neutral.base**k)
1567
+ end
1568
+ x = -x if neutral.sign=='-'
1569
+ elsif !honor_rounding && (k>0 && (k+neutral.digits.length < 2*nd))
1570
+ j = k-neutral.digits.length
1571
+ x = neutral.digits.to_i(neutral.base).to_f * Float(neutral.base**(j))
1572
+ x *= Float(neutral.base**(k-j))
1573
+ x = -x if neutral.sign=='-'
1574
+ elsif neutral.base.modulo(Float::RADIX)==0
1575
+
1576
+ f = neutral.digits.to_i(neutral.base)
1577
+ e = neutral.dec_pos-neutral.digits.length
1578
+
1579
+ rounding = neutral.rounding
1580
+
1581
+ x = Nio::Clinger::algM(f,e,rounding,neutral.base,Float::RADIX,Float::MANT_DIG,Float::MIN_EXP-Float::MANT_DIG,Float::MAX_EXP-Float::MANT_DIG)
1582
+ x = -x if neutral.sign=='-'
1583
+
1584
+ else
1585
+
1586
+ f = neutral.digits.to_i(neutral.base)
1587
+ e = neutral.dec_pos-neutral.digits.length
1588
+
1589
+ rounding = neutral.rounding
1590
+
1591
+ x = Nio::Clinger::algM(f,e,rounding,neutral.base,Float::RADIX,Float::MANT_DIG,Float::MIN_EXP-Float::MANT_DIG,Float::MAX_EXP-Float::MANT_DIG)
1592
+ x = -x if neutral.sign=='-'
1593
+
1594
+ end
1595
+ end
1596
+
1597
+ return x
1598
+ end
1599
+ def nio_write_neutral(fmt)
1600
+ neutral = Nio::NeutralNum.new
1601
+ x = self
1602
+
1603
+ if x.nan?
1604
+ neutral.set_special(:nan)
1605
+ elsif x.infinite?
1606
+ neutral.set_special(:inf, x<0 ? '-' : '+')
1607
+ else
1608
+ converted = false
1609
+ if fmt.get_ndig==:exact && fmt.get_approx==:simplify
1610
+
1611
+ if x!=0
1612
+ q = x.nio_r(Nio::Tolerance.decimals(Float::DIG,:sig))
1613
+ if q!=0
1614
+ neutral = q.nio_write_neutral(fmt)
1615
+ converted = true if neutral.digits.length<=Float::DIG
1616
+ end
1617
+ end
1618
+
1619
+ elsif fmt.get_approx==:exact
1620
+ neutral = x.nio_xr.nio_write_neutral(fmt)
1621
+ converted = true
1622
+ end
1623
+ if !converted
1624
+ if fmt.get_base==10 && false
1625
+ txt = format "%.*e",Float::DECIMAL_DIG-1,x # note that spec. e output precision+1 significant digits
1626
+
1627
+ sign = '+'
1628
+ if txt[0,1]=='-'
1629
+ sign = '-'
1630
+ txt = txt[1...txt.length]
1631
+ end
1632
+ exp = 0
1633
+ x_char = fmt.get_exp_char(fmt.get_base)
1634
+
1635
+ exp_i = txt.index(x_char)
1636
+ exp_i = txt.index(x_char.downcase) if exp_i===nil
1637
+ if exp_i!=nil
1638
+ exp = txt[exp_i+1...txt.length].to_i
1639
+ txt = txt[0...exp_i]
1640
+ end
1641
+
1642
+ dec_pos = txt.index '.'
1643
+ if dec_pos==nil
1644
+ dec_pos = txt.length
1645
+ else
1646
+ txt[dec_pos]=''
1647
+ end
1648
+ dec_pos += exp
1649
+ neutral.set sign, txt, dec_pos, nil, fmt.get_base_digits(10), true, fmt.get_round
1650
+
1651
+ converted = true
1652
+ end
1653
+ end
1654
+ if !converted
1655
+
1656
+ sign = x<0 ? '-' : '+'
1657
+ x = -x if sign=='-'
1658
+ f,e = Math.frexp(x)
1659
+ if e < Float::MIN_EXP
1660
+ # denormalized number
1661
+ f = Math.ldexp(f,e-Float::MIN_EXP+Float::MANT_DIG)
1662
+ e = Float::MIN_EXP-Float::MANT_DIG
1663
+ else
1664
+ # normalized number
1665
+ f = Math.ldexp(f,Float::MANT_DIG)
1666
+ e -= Float::MANT_DIG
1667
+ end
1668
+ f = f.to_i
1669
+ inexact = true
1670
+
1671
+ rounding = fmt.get_round
1672
+
1673
+ if fmt.get_all_digits?
1674
+ # use as many digits as possible
1675
+ dec_pos,r,*digits = Nio::BurgerDybvig::float_to_digits_max(x,f,e,rounding,Float::MIN_EXP-Float::MANT_DIG,Float::MANT_DIG,Float::RADIX,fmt.get_base)
1676
+ inexact = :roundup if r
1677
+ else
1678
+ # use as few digits as possible
1679
+ dec_pos,*digits = Nio::BurgerDybvig::float_to_digits(x,f,e,rounding,Float::MIN_EXP-Float::MANT_DIG,Float::MANT_DIG,Float::RADIX,fmt.get_base)
1680
+ end
1681
+ txt = ''
1682
+ digits.each{|d| txt << fmt.get_base_digits.digit_char(d)}
1683
+ neutral.set sign, txt, dec_pos, nil, fmt.get_base_digits, inexact, fmt.get_round
1684
+
1685
+ end
1686
+ end
1687
+
1688
+ return neutral
1689
+ end
1690
+ end
1691
+
1692
+ class Numeric
1693
+ unless method_defined?(:even?)
1694
+ def even?
1695
+ self.modulo(2)==0
1696
+ end
1697
+ end
1698
+ unless method_defined?(:odd?)
1699
+ def odd?
1700
+ self.modulo(2)!=0
1701
+ end
1702
+ end
1703
+ end
1704
+
1705
+ class Integer
1706
+ include Nio::Formattable
1707
+ def self.nio_read_neutral(neutral)
1708
+ x = nil
1709
+
1710
+ if neutral.special?
1711
+ raise Nio::InvalidFormat,"Invalid integer numeral"
1712
+ elsif neutral.rep_pos<neutral.digits.length
1713
+ return Rational.nio_read_neutral(neutral).to_i
1714
+ else
1715
+ digits = neutral.digits
1716
+
1717
+ if neutral.dec_pos <= 0
1718
+ digits = '0'
1719
+ elsif neutral.dec_pos <= digits.length
1720
+ digits = digits[0...neutral.dec_pos]
1721
+ else
1722
+ digits = digits + '0'*(neutral.dec_pos-digits.length)
1723
+ end
1724
+
1725
+ x = digits.to_i(neutral.base)
1726
+ # this was formely needed because we didn't adust the digits
1727
+ # if neutral.dec_pos != neutral.digits.length
1728
+ # # with rational included, negative powers of ten are rational numbers
1729
+ # x = (x*((neutral.base)**(neutral.dec_pos-neutral.digits.length))).to_i
1730
+ # end
1731
+ x = -x if neutral.sign=='-'
1732
+ end
1733
+
1734
+ return x
1735
+ end
1736
+ def nio_write_neutral(fmt)
1737
+ neutral = Nio::NeutralNum.new
1738
+ x = self
1739
+
1740
+ sign = x<0 ? '-' : '+'
1741
+ txt = x.abs.to_s(fmt.get_base)
1742
+ dec_pos = rep_pos = txt.length
1743
+ neutral.set sign, txt, dec_pos, nil, fmt.get_base_digits, false ,fmt.get_round
1744
+
1745
+ return neutral
1746
+ end
1747
+ end
1748
+
1749
+ class Rational
1750
+ include Nio::Formattable
1751
+ def self.nio_read_neutral(neutral)
1752
+ x = nil
1753
+
1754
+ if neutral.special?
1755
+ case neutral.special
1756
+ when :nan
1757
+ x = Rational(0,0)
1758
+ when :inf
1759
+ x = Rational((neutral.sign=='-' ? -1 : +1),0)
1760
+ end
1761
+ else
1762
+ x = Rational(*neutral.to_RepDec.getQ)
1763
+ end
1764
+
1765
+ return x
1766
+ end
1767
+ def nio_write_neutral(fmt)
1768
+ neutral = Nio::NeutralNum.new
1769
+ x = self
1770
+
1771
+ if x.denominator==0
1772
+ if x.numerator>0
1773
+ neutral.set_special(:inf)
1774
+ elsif x.numerator<0
1775
+ neutral.set_special(:inf,'-')
1776
+ else
1777
+ neutral.set_special(:nan)
1778
+ end
1779
+ else
1780
+ if fmt.get_base==10
1781
+ rd = Nio::RepDec.new.setQ(x.numerator,x.denominator)
1782
+ else
1783
+ opt = Nio::RepDec::DEF_OPT.dup.set_digits(fmt.get_base_digits)
1784
+ rd = Nio::RepDec.new.setQ(x.numerator,x.denominator, opt)
1785
+ end
1786
+ neutral = rd.to_NeutralNum(fmt.get_base_digits)
1787
+ neutral.rounding = fmt.get_round
1788
+ end
1789
+
1790
+ return neutral
1791
+ end
1792
+ end
1793
+
1794
+ if defined? BigDecimal
1795
+ class BigDecimal
1796
+ include Nio::Formattable
1797
+ def self.nio_read_neutral(neutral)
1798
+ x = nil
1799
+
1800
+ if neutral.special?
1801
+ case neutral.special
1802
+ when :nan
1803
+ x = BigDecimal('NaN') # BigDecimal("0")/0
1804
+ when :inf
1805
+ x = BigDecimal(neutral.sign=='-' ? '-1.0' : '+1.0')/0
1806
+ end
1807
+ elsif neutral.rep_pos<neutral.digits.length
1808
+
1809
+ x,y = neutral.to_RepDec.getQ
1810
+ x = BigDecimal(x.to_s)/y
1811
+
1812
+ else
1813
+ if neutral.base==10
1814
+ #x = BigDecimal(neutral.digits)
1815
+ #x *= BigDecimal("1E#{(neutral.dec_pos-neutral.digits.length)}")
1816
+ #x = -x if neutral.sign=='-'
1817
+ str = neutral.sign
1818
+ str += neutral.digits
1819
+ str += "E#{(neutral.dec_pos-neutral.digits.length)}"
1820
+ x = BigDecimal(str)
1821
+ else
1822
+ x = BigDecimal(neutral.digits.to_i(neutral.base).to_s)
1823
+ x *= BigDecimal(neutral.base.to_s)**(neutral.dec_pos-neutral.digits.length)
1824
+ x = -x if neutral.sign=='-'
1825
+ end
1826
+ end
1827
+
1828
+ return x
1829
+ end
1830
+ def nio_write_neutral(fmt)
1831
+ neutral = Nio::NeutralNum.new
1832
+ x = self
1833
+
1834
+ if x.nan?
1835
+ neutral.set_special(:nan)
1836
+ elsif x.infinite?
1837
+ neutral.set_special(:inf, x<0 ? '-' : '+')
1838
+ else
1839
+ converted = false
1840
+ if fmt.get_ndig==:exact && fmt.get_approx==:simplify
1841
+
1842
+ prc = [x.precs[0],20].max
1843
+ neutral = x.nio_r(Nio::BigTolerance.decimals(prc,:sig)).nio_write_neutral(fmt)
1844
+ converted = true if neutral.digits.length<prc
1845
+
1846
+ elsif fmt.get_approx==:exact && fmt.get_base!=10
1847
+ neutral = x.nio_xr.nio_write_neutral(fmt)
1848
+ converted = true
1849
+ end
1850
+ if !converted
1851
+ if fmt.get_base==10
1852
+ txt = x.to_s
1853
+
1854
+ sign = '+'
1855
+ if txt[0,1]=='-'
1856
+ sign = '-'
1857
+ txt = txt[1...txt.length]
1858
+ end
1859
+ exp = 0
1860
+ x_char = fmt.get_exp_char(fmt.get_base)
1861
+
1862
+ exp_i = txt.index(x_char)
1863
+ exp_i = txt.index(x_char.downcase) if exp_i===nil
1864
+ if exp_i!=nil
1865
+ exp = txt[exp_i+1...txt.length].to_i
1866
+ txt = txt[0...exp_i]
1867
+ end
1868
+
1869
+ dec_pos = txt.index '.'
1870
+ if dec_pos==nil
1871
+ dec_pos = txt.length
1872
+ else
1873
+ txt[dec_pos]=''
1874
+ end
1875
+ dec_pos += exp
1876
+ neutral.set sign, txt, dec_pos, nil, fmt.get_base_digits(10), true, fmt.get_round
1877
+
1878
+ converted = true
1879
+ end
1880
+ end
1881
+ if !converted
1882
+
1883
+ min_prec = 24
1884
+ min_exp = -1000
1885
+ s,f,b,e = x.split
1886
+ e -= f.size
1887
+ sign = s<0 ? '-' : '+'
1888
+ x = -x if sign=='-'
1889
+ f_i = f.to_i
1890
+ prc = [x.precs[0],min_prec].max
1891
+ f_i *= 10**(prc-f.size)
1892
+ e -= (prc-f.size)
1893
+
1894
+ inexact = true
1895
+
1896
+ rounding = fmt.get_round
1897
+
1898
+ if fmt.get_all_digits?
1899
+ # use as many digits as possible
1900
+ dec_pos,r,*digits = Nio::BurgerDybvig::float_to_digits_max(x,f_i,e,rounding,[e,min_exp].min,prc,b,fmt.get_base)
1901
+ inexact = :roundup if r
1902
+ else
1903
+ # use as few digits as possible
1904
+ dec_pos,*digits = Nio::BurgerDybvig::float_to_digits(x,f_i,e,rounding,[e,min_exp].min,prc,b,fmt.get_base)
1905
+ end
1906
+ txt = ''
1907
+ digits.each{|d| txt << fmt.get_base_digits.digit_char(d)}
1908
+ neutral.set sign, txt, dec_pos, nil, fmt.get_base_digits, inexact, fmt.get_round
1909
+
1910
+ end
1911
+ end
1912
+
1913
+ return neutral
1914
+ end
1915
+ end
1916
+ end
1917
+