nio 0.2.1 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
+