nio 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +5 -0
- data/License.txt +20 -0
- data/Manifest.txt +34 -0
- data/README.txt +560 -0
- data/Rakefile +4 -0
- data/SOURCE.txt +31 -0
- data/config/hoe.rb +73 -0
- data/config/requirements.rb +17 -0
- data/lib/nio/flttol.rb +654 -0
- data/lib/nio/fmt.rb +1872 -0
- data/lib/nio/repdec.rb +496 -0
- data/lib/nio/rtnlzr.rb +406 -0
- data/lib/nio/sugar.rb +99 -0
- data/lib/nio/tools.rb +44 -0
- data/lib/nio/version.rb +9 -0
- data/lib/nio.rb +8 -0
- data/log/debug.log +0 -0
- data/script/destroy +14 -0
- data/script/destroy.cmd +1 -0
- data/script/generate +14 -0
- data/script/generate.cmd +1 -0
- data/script/txt2html +74 -0
- data/script/txt2html.cmd +1 -0
- data/setup.rb +1585 -0
- data/tasks/deployment.rake +27 -0
- data/tasks/environment.rake +7 -0
- data/tasks/nuweb.rake +69 -0
- data/tasks/website.rake +17 -0
- data/test/data.yaml +101 -0
- data/test/test_fmt.rb +373 -0
- data/test/test_helper.rb +32 -0
- data/test/test_repdec.rb +88 -0
- data/test/test_rtnlzr.rb +125 -0
- data/test/test_tools.rb +40 -0
- metadata +88 -0
data/lib/nio/fmt.rb
ADDED
@@ -0,0 +1,1872 @@
|
|
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 towards infinite; 1.5 is rounded to 2, -1.5 to -2
|
526
|
+
# [<tt>:zero</tt>]
|
527
|
+
# rounds towards zero; 1.5 is rounded to 1, -1.5 to 2
|
528
|
+
# [<tt>:even</tt>]
|
529
|
+
# rounds to the nearest even digit 1.5 rounds to 2, 2.5 to 2
|
530
|
+
# - <tt>:approx</tt> approximate mode
|
531
|
+
# [<tt>:only_sig</tt>]
|
532
|
+
# (the default) treats the value as an approximation and only
|
533
|
+
# significant digits (those that cannot take an arbitrary value without
|
534
|
+
# changing the specified value) are shown.
|
535
|
+
# [<tt>:exact</tt>]
|
536
|
+
# the value is interpreted as exact, there's no distinction between
|
537
|
+
# significant and insignificant digits.
|
538
|
+
# [<tt>:simplify</tt>]
|
539
|
+
# the value is simplified, if possible to a simpler (rational) value.
|
540
|
+
# - <tt>:show_all_digits</tt> if true, this forces to show digits that
|
541
|
+
# would otherwise not be shown in the <tt>:gen</tt> format: trailing
|
542
|
+
# zeros of exact types or non-signficative digits of inexact types.
|
543
|
+
# - <tt>:nonsignficative_digits</tt> assigns a character to display
|
544
|
+
# insignificant digits, # by default
|
545
|
+
def mode(mode,precision=nil,options={})
|
546
|
+
dup.mode!(mode,precision,options)
|
547
|
+
end
|
548
|
+
# This is the mutator version of #mode().
|
549
|
+
def mode!(mode,precision=nil,options={})
|
550
|
+
set! options.merge(:mode=>mode, :ndig=>precision)
|
551
|
+
end
|
552
|
+
|
553
|
+
# Defines the formatting mode like #mode() but using a different
|
554
|
+
# order of the first two parameters parameters, which is useful
|
555
|
+
# to change the precision only. Refer to #mode().
|
556
|
+
def prec(precision,mode=nil, options={})
|
557
|
+
dup.prec! precision, mode, options
|
558
|
+
end
|
559
|
+
# This is the mutator version of #prec().
|
560
|
+
def prec!(precision,mode=:gen, options={})
|
561
|
+
set! options.merge(:mode=>mode, :ndig=>precision)
|
562
|
+
end
|
563
|
+
|
564
|
+
# This is a shortcut to return a new default Fmt object
|
565
|
+
# and define the formatting mode as with #mode()
|
566
|
+
def Fmt.mode(mode,ndig=nil,options={})
|
567
|
+
Fmt.default.mode(mode,ndig,options)
|
568
|
+
end
|
569
|
+
# This is a shortcut to return a new default Fmt object
|
570
|
+
# and define the formatting mode as with #prec()
|
571
|
+
def Fmt.prec(ndig,mode=nil,options={})
|
572
|
+
Fmt.default.prec(ndig,mode,options)
|
573
|
+
end
|
574
|
+
|
575
|
+
# Rounding mode used when not specified otherwise
|
576
|
+
def Fmt.default_rounding_mode
|
577
|
+
@@default_rounding_mode
|
578
|
+
end
|
579
|
+
# The default rounding can be changed here; it starts with the value :even.
|
580
|
+
# See the rounding modes available in the description of method #mode().
|
581
|
+
def Fmt.default_rounding_mode=(m)
|
582
|
+
@@default_rounding_mode=m
|
583
|
+
Fmt.default = Fmt.default.round(m)
|
584
|
+
end
|
585
|
+
|
586
|
+
# This controls the display of the digits that are not necessary
|
587
|
+
# to specify the value unambiguosly (e.g. trailing zeros).
|
588
|
+
#
|
589
|
+
# The true (default) value forces the display of the requested number of digits
|
590
|
+
# and false will display only necessary digits.
|
591
|
+
def show_all_digits(ad=true)
|
592
|
+
dup.show_all_digits! ad
|
593
|
+
end
|
594
|
+
# This is the mutator version of #show_all_digits().
|
595
|
+
def show_all_digits!(ad=true)
|
596
|
+
set! :all_digits=>ad
|
597
|
+
end
|
598
|
+
# This defines the approximate mode (:only_sig, :exact, :simplify)
|
599
|
+
# just like the last parameter of #mode()
|
600
|
+
def approx_mode(mode)
|
601
|
+
dup.approx_mode! mode
|
602
|
+
end
|
603
|
+
# This is the mutator version of #approx_mode().
|
604
|
+
def approx_mode!(mode)
|
605
|
+
set! :approx=>mode
|
606
|
+
end
|
607
|
+
# Defines a character to stand for insignificant digits when
|
608
|
+
# a specific number of digits has been requested greater than then
|
609
|
+
# number of significant digits (for approximate types).
|
610
|
+
def insignificant_digits(ch='#')
|
611
|
+
dup.insignificant_digits! ch
|
612
|
+
end
|
613
|
+
# This is the mutator version of #insignificant_digits().
|
614
|
+
def insignificant_digits!(ch='#')
|
615
|
+
ch ||= ''
|
616
|
+
set! :non_sig=>ch
|
617
|
+
end
|
618
|
+
# Defines the number of significan digits before the radix separator
|
619
|
+
# in scientific notation. A negative value will set all significant digits
|
620
|
+
# before the radix separator. The special value <tt>:eng</tt> activates
|
621
|
+
# _engineering_ mode, in which the exponents are multiples of 3.
|
622
|
+
#
|
623
|
+
# For example:
|
624
|
+
# 0.1234.nio_write(Fmt.mode(:sci,4).sci_digits(0) -> 0.1234E0
|
625
|
+
# 0.1234.nio_write(Fmt.mode(:sci,4).sci_digits(3) -> 123.4E-3
|
626
|
+
# 0.1234.nio_write(Fmt.mode(:sci,4).sci_digits(-1) -> 1234.E-4
|
627
|
+
# 0.1234.nio_write(Fmt.mode(:sci,4).sci_digits(:eng) -> 123.4E-3
|
628
|
+
def sci_digits(n=-1)
|
629
|
+
dup.sci_digits! n
|
630
|
+
end
|
631
|
+
# This is the mutator version of #sci_digits().
|
632
|
+
def sci_digits!(n=-1)
|
633
|
+
set! :sci_format=>n
|
634
|
+
end
|
635
|
+
|
636
|
+
# This is a shortcut to return a new default Fmt object
|
637
|
+
# and define show_all_digits
|
638
|
+
def Fmt.show_all_digits(v=true)
|
639
|
+
Fmt.default.show_all_digits(v)
|
640
|
+
end
|
641
|
+
# This is a shortcut to return a new default Fmt object
|
642
|
+
# and define approx_mode
|
643
|
+
def Fmt.approx_mode(v)
|
644
|
+
Fmt.default.approx_mode(v)
|
645
|
+
end
|
646
|
+
# This is a shortcut to return a new default Fmt object
|
647
|
+
# and define insignificant digits
|
648
|
+
def Fmt.insignificant_digits(v='#')
|
649
|
+
Fmt.default.insignificant_digits(v)
|
650
|
+
end
|
651
|
+
# This is a shortcut to return a new default Fmt object
|
652
|
+
# and define sci_digits
|
653
|
+
def Fmt.sci_digits(v=-1)
|
654
|
+
Fmt.default.sci_digits(v)
|
655
|
+
end
|
656
|
+
|
657
|
+
# Controls the display of the sign for positive numbers
|
658
|
+
def show_plus(sp=true)
|
659
|
+
dup.show_plus! sp
|
660
|
+
end
|
661
|
+
# This is the mutator version of #show_plus().
|
662
|
+
def show_plus!(sp=true)
|
663
|
+
set! :show_plus=>sp
|
664
|
+
end
|
665
|
+
|
666
|
+
# This is a shortcut to return a new default Fmt object
|
667
|
+
# and define show_plus
|
668
|
+
def Fmt.show_plus(v=true)
|
669
|
+
Fmt.default.show_plus(v)
|
670
|
+
end
|
671
|
+
|
672
|
+
# Defines the handling and notation for repeating numerals. The parameters
|
673
|
+
# can be passed in order or in a hash:
|
674
|
+
# [<tt>:begin</tt>] is the beginning delimiter of repeating section (<)
|
675
|
+
# [<tt>:end</tt>] is the ending delimiter of repeating section (<)
|
676
|
+
# [<tt>:suffix</tt>] is the suffix used to indicate a implicit repeating decimal
|
677
|
+
# [<tt>:rep</tt>]
|
678
|
+
# if this parameter is greater than zero, on output the repeating section
|
679
|
+
# is repeated the indicated number of times followed by the suffix;
|
680
|
+
# otherwise the delimited notation is used.
|
681
|
+
# [<tt>:read</tt>]
|
682
|
+
# (true/false) determines if repeating decimals are
|
683
|
+
# recognized on input (true)
|
684
|
+
def rep(*params)
|
685
|
+
dup.rep!(*params)
|
686
|
+
end
|
687
|
+
# This is the mutator version of #rep().
|
688
|
+
def rep!(*params)
|
689
|
+
|
690
|
+
params << {} if params.size==0
|
691
|
+
if params[0].kind_of?(Hash)
|
692
|
+
params = params[0]
|
693
|
+
else
|
694
|
+
begch,endch,autoch,rep,read = *params
|
695
|
+
params = {:begin=>begch,:end=>endch,:suffix=>autoch,:nreps=>rep,:read=>read}
|
696
|
+
end
|
697
|
+
|
698
|
+
set! params
|
699
|
+
end
|
700
|
+
|
701
|
+
# This is a shortcut to return a new default Fmt object
|
702
|
+
# and define the repeating decimals mode as with #rep()
|
703
|
+
def Fmt.rep(*params)
|
704
|
+
Fmt.default.rep(*params)
|
705
|
+
end
|
706
|
+
|
707
|
+
# Sets the justificaton width, mode and fill character
|
708
|
+
#
|
709
|
+
# The mode accepts these values:
|
710
|
+
# [<tt>:right</tt>] (the default) justifies to the right (adds padding at the left)
|
711
|
+
# [<tt>:left</tt>] justifies to the left (adds padding to the right)
|
712
|
+
# [<tt>:internal</tt>] like :right, but the sign is kept to the left, outside the padding.
|
713
|
+
# [<tt>:center</tt>] centers the number in the field
|
714
|
+
def width(w,adj=nil,ch=nil)
|
715
|
+
dup.width! w,adj,ch
|
716
|
+
end
|
717
|
+
# This is the mutator version of #width().
|
718
|
+
def width!(w,adj=nil,ch=nil)
|
719
|
+
set! :width=>w, :adjust=>adj, :fill_char=>ch
|
720
|
+
end
|
721
|
+
# Defines the justification (as #width()) with the given
|
722
|
+
# width, internal mode and filling with zeros.
|
723
|
+
#
|
724
|
+
# Note that if you also use grouping separators, the filling 0s
|
725
|
+
# will not be separated.
|
726
|
+
def pad0s(w)
|
727
|
+
dup.pad0s! w
|
728
|
+
end
|
729
|
+
# This is the mutator version of #pad0s().
|
730
|
+
def pad0s!(w)
|
731
|
+
width! w, :internal, '0'
|
732
|
+
end
|
733
|
+
# This is a shortcut to create a new Fmt object and define the width
|
734
|
+
# parameters as with #widht()
|
735
|
+
def Fmt.width(w,adj=nil,ch=nil)
|
736
|
+
Fmt.default.width(w,adj,ch)
|
737
|
+
end
|
738
|
+
# This is a shortcut to create a new Fmt object and define numeric
|
739
|
+
# padding as with #pad0s()
|
740
|
+
def Fmt.pad0s(w)
|
741
|
+
Fmt.default.pad0s(w)
|
742
|
+
end
|
743
|
+
|
744
|
+
# defines the numerical base; the second parameters forces the use
|
745
|
+
# of uppercase letters for bases greater than 10.
|
746
|
+
def base(b, uppercase=nil)
|
747
|
+
dup.base! b, uppercase
|
748
|
+
end
|
749
|
+
# This is the mutator version of #base().
|
750
|
+
def base!(b, uppercase=nil)
|
751
|
+
set! :base_radix=>b, :base_uppercase=>uppercase
|
752
|
+
end
|
753
|
+
# This is a shortcut to create a new Fmt object and define the base
|
754
|
+
def Fmt.base(b, uppercase=nil)
|
755
|
+
Fmt.default.base(b, uppercase)
|
756
|
+
end
|
757
|
+
# returns the exponent char used with the specified base
|
758
|
+
def get_exp_char(base) # :nodoc:
|
759
|
+
base ||= @base_radix
|
760
|
+
base<=10 ? 'E' : '^'
|
761
|
+
end
|
762
|
+
|
763
|
+
# returns the base
|
764
|
+
def get_base # :nodoc:
|
765
|
+
@base_radix
|
766
|
+
end
|
767
|
+
# returns the digit characters used for a base
|
768
|
+
def get_base_digits(b=nil) # :nodoc:
|
769
|
+
(b.nil? || b==@base_radix) ? @base_digits : DigitsDef.base(b,!@base_uppercase)
|
770
|
+
end
|
771
|
+
# returns true if uppercase digits are used
|
772
|
+
def get_base_uppercase? # :nodoc:
|
773
|
+
@base_uppercase
|
774
|
+
end
|
775
|
+
|
776
|
+
# returns the formatting mode
|
777
|
+
def get_mode # :nodoc:
|
778
|
+
@mode
|
779
|
+
end
|
780
|
+
# returns the precision (number of digits)
|
781
|
+
def get_ndig # :nodoc:
|
782
|
+
@ndig
|
783
|
+
end
|
784
|
+
# return the show_all_digits state
|
785
|
+
def get_all_digits? # :nodoc:
|
786
|
+
@all_digits
|
787
|
+
end
|
788
|
+
# returns the approximate mode
|
789
|
+
def get_approx # :nodoc:
|
790
|
+
@approx
|
791
|
+
end
|
792
|
+
|
793
|
+
# returns the rounding mode
|
794
|
+
def get_round # :nodoc:
|
795
|
+
@round
|
796
|
+
end
|
797
|
+
|
798
|
+
# Method used internally to format a neutral numeral
|
799
|
+
def nio_write_formatted(neutral) # :nodoc:
|
800
|
+
str = ''
|
801
|
+
if neutral.special?
|
802
|
+
str << neutral.sign
|
803
|
+
case neutral.special
|
804
|
+
when :inf
|
805
|
+
str << @inf_txt
|
806
|
+
when :nan
|
807
|
+
str << @nan_txt
|
808
|
+
end
|
809
|
+
else
|
810
|
+
zero = get_base_digits(neutral.base).digit_char(0).chr
|
811
|
+
neutral = neutral.dup
|
812
|
+
round! neutral
|
813
|
+
if neutral.zero?
|
814
|
+
str << neutral.sign if neutral.sign=='-' # show - if number was <0 before rounding
|
815
|
+
str << zero
|
816
|
+
if @ndig.kind_of?(Numeric) && @ndig>0 && @mode==:fix
|
817
|
+
str << @dec_sep << zero*@ndig
|
818
|
+
end
|
819
|
+
else
|
820
|
+
|
821
|
+
neutral.trimLeadZeros
|
822
|
+
actual_mode = @mode
|
823
|
+
trim_trail_zeros = !@all_digits # false
|
824
|
+
|
825
|
+
integral_digits = @sci_format
|
826
|
+
if integral_digits == :eng
|
827
|
+
integral_digits = 1
|
828
|
+
while (neutral.dec_pos - integral_digits).modulo(3) != 0
|
829
|
+
integral_digits += 1
|
830
|
+
end
|
831
|
+
elsif integral_digits==:all || integral_digits < 0
|
832
|
+
if neutral.inexact? && @non_sig!='' && @ndig.kind_of?(Numeric)
|
833
|
+
integral_digits = @ndig
|
834
|
+
else
|
835
|
+
integral_digits = neutral.digits.length
|
836
|
+
end
|
837
|
+
end
|
838
|
+
exp = neutral.dec_pos - integral_digits
|
839
|
+
|
840
|
+
case actual_mode
|
841
|
+
when :gen # general (automatic)
|
842
|
+
# @ndig means significant digits
|
843
|
+
actual_mode = :sig
|
844
|
+
actual_mode = :sci if use_scientific?(neutral, exp)
|
845
|
+
trim_trail_zeros = !@all_digits # true
|
846
|
+
end
|
847
|
+
|
848
|
+
case actual_mode
|
849
|
+
when :fix, :sig #, :gen
|
850
|
+
|
851
|
+
str << neutral.sign if @show_plus || neutral.sign!='+'
|
852
|
+
|
853
|
+
if @show_base && @base_prefix
|
854
|
+
b_prefix = @base_indicators[neutral.base]
|
855
|
+
str << b_prefix if b_prefix
|
856
|
+
end
|
857
|
+
|
858
|
+
if @ndig==:exact
|
859
|
+
neutral.sign = '+'
|
860
|
+
str << neutral.to_RepDec.getS(@rep_n, getRepDecOpt(neutral.base))
|
861
|
+
else
|
862
|
+
#zero = get_base_digits.digit_char(0).chr
|
863
|
+
ns_digits = ''
|
864
|
+
|
865
|
+
nd = neutral.digits.length
|
866
|
+
if actual_mode==:fix
|
867
|
+
nd -= neutral.dec_pos
|
868
|
+
end
|
869
|
+
if neutral.inexact? && @ndig>nd # assert no rep-dec.
|
870
|
+
ns_digits = @non_sig*(@ndig-nd)
|
871
|
+
end
|
872
|
+
|
873
|
+
digits = neutral.digits + ns_digits
|
874
|
+
if neutral.dec_pos<=0
|
875
|
+
str << zero+@dec_sep+zero*(-neutral.dec_pos) + digits
|
876
|
+
elsif neutral.dec_pos >= digits.length
|
877
|
+
str << group(digits + zero*(neutral.dec_pos-digits.length))
|
878
|
+
else
|
879
|
+
str << group(digits[0...neutral.dec_pos]) + @dec_sep + digits[neutral.dec_pos..-1]
|
880
|
+
end
|
881
|
+
end
|
882
|
+
|
883
|
+
#str = str.chomp(zero).chomp(@dec_sep) if trim_trail_zeros && str.include?(@dec_sep)
|
884
|
+
if trim_trail_zeros && str.include?(@dec_sep) && str[-@rep_auto.size..-1]!=@rep_auto
|
885
|
+
str.chop! while str[-1]==zero[0]
|
886
|
+
str.chomp!(@dec_sep)
|
887
|
+
#puts str
|
888
|
+
end
|
889
|
+
|
890
|
+
|
891
|
+
when :sci
|
892
|
+
|
893
|
+
str << neutral.sign if @show_plus || neutral.sign!='+'
|
894
|
+
|
895
|
+
if @show_base && @base_prefix
|
896
|
+
b_prefix = @base_indicators[neutral.base]
|
897
|
+
str << b_prefix if b_prefix
|
898
|
+
end
|
899
|
+
|
900
|
+
#zero = get_base_digits.digit_char(0).chr
|
901
|
+
if @ndig==:exact
|
902
|
+
neutral.sign = '+'
|
903
|
+
neutral.dec_pos-=exp
|
904
|
+
str << neutral.to_RepDec.getS(@rep_n, getRepDecOpt(neutral.base))
|
905
|
+
else
|
906
|
+
ns_digits = ''
|
907
|
+
|
908
|
+
nd = neutral.digits.length
|
909
|
+
if actual_mode==:fix
|
910
|
+
nd -= neutral.dec_pos
|
911
|
+
end
|
912
|
+
if neutral.inexact? && @ndig>nd # assert no rep-dec.
|
913
|
+
ns_digits = @non_sig*(@ndig-nd)
|
914
|
+
end
|
915
|
+
|
916
|
+
digits = neutral.digits + ns_digits
|
917
|
+
str << ((integral_digits<1) ? zero : digits[0...integral_digits])
|
918
|
+
str << @dec_sep
|
919
|
+
str << digits[integral_digits...@ndig]
|
920
|
+
pad_right =(@ndig+1-str.length)
|
921
|
+
str << zero*pad_right if pad_right>0 && !neutral.inexact? # maybe we didn't have enought digits
|
922
|
+
end
|
923
|
+
|
924
|
+
#str = str.chomp(zero).chomp(@dec_sep) if trim_trail_zeros && str.include?(@dec_sep)
|
925
|
+
if trim_trail_zeros && str.include?(@dec_sep) && str[-@rep_auto.size..-1]!=@rep_auto
|
926
|
+
str.chop! while str[-1]==zero[0]
|
927
|
+
str.chomp!(@dec_sep)
|
928
|
+
#puts str
|
929
|
+
end
|
930
|
+
|
931
|
+
str << get_exp_char(neutral.base)
|
932
|
+
str << exp.to_s
|
933
|
+
|
934
|
+
end
|
935
|
+
|
936
|
+
end
|
937
|
+
end
|
938
|
+
|
939
|
+
if @show_base && !@base_prefix
|
940
|
+
b_prefix = @base_indicators[neutral.base]
|
941
|
+
str << b_prefix if b_prefix
|
942
|
+
end
|
943
|
+
|
944
|
+
|
945
|
+
if @width>0 && @fill_char!=''
|
946
|
+
l = @width - str.length
|
947
|
+
if l>0
|
948
|
+
case @adjust
|
949
|
+
when :internal
|
950
|
+
sign = ''
|
951
|
+
if str[0,1]=='+' || str[0,1]=='-'
|
952
|
+
sign = str[0,1]
|
953
|
+
str = str[1...str.length]
|
954
|
+
end
|
955
|
+
str = sign + @fill_char*l + str
|
956
|
+
when :center
|
957
|
+
str = @fill_char*(l/2) + str + @fill_char*(l-l/2)
|
958
|
+
when :right
|
959
|
+
str = @fill_char*l + str
|
960
|
+
when :left
|
961
|
+
str = str + @fill_char*l
|
962
|
+
end
|
963
|
+
end
|
964
|
+
end
|
965
|
+
|
966
|
+
return str
|
967
|
+
end
|
968
|
+
|
969
|
+
# round a neutral numeral according to the format options
|
970
|
+
def round!(neutral) # :nodoc:
|
971
|
+
neutral.round! @ndig, @mode, @round
|
972
|
+
end
|
973
|
+
|
974
|
+
@@sci_fmt = nil
|
975
|
+
|
976
|
+
def nio_read_formatted(txt) # :nodoc:
|
977
|
+
txt = txt.dup
|
978
|
+
num = nil
|
979
|
+
|
980
|
+
base = nil
|
981
|
+
|
982
|
+
base ||= get_base
|
983
|
+
|
984
|
+
zero = get_base_digits(base).digit_char(0).chr
|
985
|
+
txt.tr!(@non_sig,zero) # we don't simply remove it because it may be before the radix point
|
986
|
+
|
987
|
+
exp = 0
|
988
|
+
x_char = get_exp_char(base)
|
989
|
+
|
990
|
+
exp_i = txt.index(x_char)
|
991
|
+
exp_i = txt.index(x_char.downcase) if exp_i===nil
|
992
|
+
if exp_i!=nil
|
993
|
+
exp = txt[exp_i+1...txt.length].to_i
|
994
|
+
txt = txt[0...exp_i]
|
995
|
+
end
|
996
|
+
|
997
|
+
|
998
|
+
opt = getRepDecOpt(base)
|
999
|
+
if @rep_in
|
1000
|
+
#raise InvalidFormat,"Invalid numerical base" if base!=10
|
1001
|
+
rd = RepDec.new # get_base not necessary: setS sets it from options
|
1002
|
+
rd.setS txt, opt
|
1003
|
+
num = rd.to_NeutralNum(opt.digits)
|
1004
|
+
else
|
1005
|
+
# to do: use RepDec.parse; then build NeutralNum directly
|
1006
|
+
opt.set_delim '',''
|
1007
|
+
opt.set_suffix ''
|
1008
|
+
rd = RepDec.new # get_base not necessary: setS sets it from options
|
1009
|
+
rd.setS txt, opt
|
1010
|
+
num = rd.to_NeutralNum(opt.digits)
|
1011
|
+
end
|
1012
|
+
num.rounding = get_round
|
1013
|
+
num.dec_pos += exp
|
1014
|
+
return num
|
1015
|
+
end
|
1016
|
+
|
1017
|
+
|
1018
|
+
@@fmts = {
|
1019
|
+
:def=>Fmt.new.freeze
|
1020
|
+
}
|
1021
|
+
# Returns the current default format.
|
1022
|
+
def self.default
|
1023
|
+
d = self[:def]
|
1024
|
+
if block_given?
|
1025
|
+
d = d.dup
|
1026
|
+
yield d
|
1027
|
+
end
|
1028
|
+
d
|
1029
|
+
end
|
1030
|
+
# Defines the current default format.
|
1031
|
+
def self.default=(fmt)
|
1032
|
+
self[:def] = fmt
|
1033
|
+
end
|
1034
|
+
# Assigns a format to a name in the formats repository.
|
1035
|
+
def self.[]=(tag,fmt_def)
|
1036
|
+
@@fmts[tag.to_sym]=fmt_def.freeze
|
1037
|
+
end
|
1038
|
+
# Retrieves a named format from the repository.
|
1039
|
+
def self.[](tag)
|
1040
|
+
@@fmts[tag.to_sym]
|
1041
|
+
end
|
1042
|
+
|
1043
|
+
protected
|
1044
|
+
|
1045
|
+
@@valid_properties = nil
|
1046
|
+
ALIAS_PROPERTIES = {
|
1047
|
+
:show_all_digits=>:all_digits,
|
1048
|
+
:rounding_mode=>:round,
|
1049
|
+
:approx_mode=>:approx,
|
1050
|
+
:sci_digits=>:sci_format,
|
1051
|
+
:non_signitificative_digits=>:non_sig,
|
1052
|
+
:begin=>:rep_begin,
|
1053
|
+
:end=>:rep_end,
|
1054
|
+
:suffix=>:rep_auto,
|
1055
|
+
:nreps=>:rep_n,
|
1056
|
+
:read=>:rep_in
|
1057
|
+
}
|
1058
|
+
def set!(properties={}) # :nodoc:
|
1059
|
+
|
1060
|
+
|
1061
|
+
@@valid_properties ||= instance_variables.collect{|v| v[1..-1].to_sym}
|
1062
|
+
|
1063
|
+
|
1064
|
+
properties.each do |k,v|
|
1065
|
+
al = ALIAS_PROPERTIES[k]
|
1066
|
+
if al
|
1067
|
+
properties[al] = v
|
1068
|
+
properties.delete k
|
1069
|
+
elsif !@@valid_properties.include?(k)
|
1070
|
+
raise InvalidOption, "Invalid option: #{k}"
|
1071
|
+
end
|
1072
|
+
end
|
1073
|
+
|
1074
|
+
|
1075
|
+
if properties[:grp_sep].nil? && !properties[:dec_sep].nil? && properties[:dec_sep]!=@dec_sep && properties[:dec_sep]==@grp_sep
|
1076
|
+
properties[:grp_sep] = properties[:dec_sep]=='.' ? ',' : '.'
|
1077
|
+
end
|
1078
|
+
|
1079
|
+
if properties[:all_digits].nil? && (properties[:ndig] || properties[:mode])
|
1080
|
+
ndig = properties[:ndig] || @ndig
|
1081
|
+
mode = properties[:mode] || @mode
|
1082
|
+
properties[:all_digits] = ndig!=:exact && mode!=:gen
|
1083
|
+
end
|
1084
|
+
|
1085
|
+
if !properties[:all_digits].nil? && properties[:non_sig].nil?
|
1086
|
+
properties[:non_sig] = '' unless properties[:all_digits]
|
1087
|
+
elsif !properties[:non_sig].nil? && properties[:all_digits].nil?
|
1088
|
+
properties[:all_digits] = true if properties[:non_sig]!=''
|
1089
|
+
end
|
1090
|
+
|
1091
|
+
if !properties[:base_radix].nil? || !properties[:base_uppercase].nil?
|
1092
|
+
base = properties[:base_radix] || @base_radix
|
1093
|
+
uppercase = properties[:base_uppercase] || @base_uppercase
|
1094
|
+
properties[:base_digits] = DigitsDef.base(base, !uppercase)
|
1095
|
+
end
|
1096
|
+
|
1097
|
+
|
1098
|
+
properties.each do |k,v|
|
1099
|
+
instance_variable_set "@#{k}", v unless v.nil?
|
1100
|
+
end
|
1101
|
+
|
1102
|
+
self
|
1103
|
+
end
|
1104
|
+
|
1105
|
+
def set(properties={}) # :nodoc:
|
1106
|
+
self.dup.set!(properties)
|
1107
|
+
end
|
1108
|
+
|
1109
|
+
def use_scientific?(neutral,exp) # :nodoc:
|
1110
|
+
nd = @ndig.kind_of?(Numeric) ? @ndig : [neutral.digits.length,10].max
|
1111
|
+
if @@sci_fmt==:hp
|
1112
|
+
puts " #{nd} ndpos=#{neutral.dec_pos} ndlen=#{neutral.digits.length}"
|
1113
|
+
neutral.dec_pos>nd || ([neutral.digits.length,nd].min-neutral.dec_pos)>nd
|
1114
|
+
else
|
1115
|
+
exp<-4 || exp>=nd
|
1116
|
+
end
|
1117
|
+
end
|
1118
|
+
|
1119
|
+
def getRepDecOpt(base=nil) # :nodoc:
|
1120
|
+
rd_opt = RepDec::Opt.new
|
1121
|
+
rd_opt.begin_rep = @rep_begin
|
1122
|
+
rd_opt.end_rep = @rep_end
|
1123
|
+
rd_opt.auto_rep = @rep_auto
|
1124
|
+
rd_opt.dec_sep = @dec_sep
|
1125
|
+
rd_opt.grp_sep = @grp_sep
|
1126
|
+
rd_opt.grp = @grp
|
1127
|
+
rd_opt.inf_txt = @inf_txt
|
1128
|
+
rd_opt.nan_txt = @nan_txt
|
1129
|
+
rd_opt.set_digits(get_base_digits(base))
|
1130
|
+
# if base && (base != get_base_digits.radix)
|
1131
|
+
# rd_opt.set_digits(get_base_digits(base))
|
1132
|
+
# else
|
1133
|
+
# rd_opt.set_digits get_base_digits
|
1134
|
+
# end
|
1135
|
+
return rd_opt
|
1136
|
+
end
|
1137
|
+
|
1138
|
+
def group(digits) # :nodoc:
|
1139
|
+
RepDec.group_digits(digits, getRepDecOpt)
|
1140
|
+
end
|
1141
|
+
|
1142
|
+
end
|
1143
|
+
|
1144
|
+
# This is a mix-in module to add formatting capabilities no numerical classes.
|
1145
|
+
# A class that includes this module should provide the methods
|
1146
|
+
# nio_write_neutral(fmt):: an instance method to write the value to
|
1147
|
+
# a neutral numeral. The format is passed so that
|
1148
|
+
# the base, for example, is available.
|
1149
|
+
# nio_read_neutral(neutral):: a class method to create a value from a neutral
|
1150
|
+
# numeral.
|
1151
|
+
module Formattable
|
1152
|
+
|
1153
|
+
# This is the method available in all formattable objects
|
1154
|
+
# to format the value into a text string according
|
1155
|
+
# to the optional format passed.
|
1156
|
+
def nio_write(fmt=Fmt.default)
|
1157
|
+
neutral = nio_write_neutral(fmt)
|
1158
|
+
fmt.nio_write_formatted(neutral)
|
1159
|
+
end
|
1160
|
+
|
1161
|
+
module ClassMethods
|
1162
|
+
# This is the method available in all formattable clases
|
1163
|
+
# to read a formatted value from a text string into
|
1164
|
+
# a value the class, according to the optional format passed.
|
1165
|
+
def nio_read(txt,fmt=Fmt.default)
|
1166
|
+
neutral = fmt.nio_read_formatted(txt)
|
1167
|
+
nio_read_neutral neutral
|
1168
|
+
end
|
1169
|
+
end
|
1170
|
+
|
1171
|
+
# Round a formattable object according to the rounding mode and
|
1172
|
+
# precision of a format.
|
1173
|
+
def nio_round(fmt=Fmt.default)
|
1174
|
+
neutral = nio_write_neutral(fmt)
|
1175
|
+
fmt.round! neutral
|
1176
|
+
self.class.nio_read_neutral neutral
|
1177
|
+
end
|
1178
|
+
|
1179
|
+
def self.append_features(mod) # :nodoc:
|
1180
|
+
super
|
1181
|
+
mod.extend ClassMethods
|
1182
|
+
end
|
1183
|
+
|
1184
|
+
end
|
1185
|
+
|
1186
|
+
Fmt[:comma] = Fmt.sep(',','.')
|
1187
|
+
Fmt[:comma_th] = Fmt.sep(',','.',[3])
|
1188
|
+
Fmt[:dot] = Fmt.sep('.',',')
|
1189
|
+
Fmt[:dot_th] = Fmt.sep('.',',',[3])
|
1190
|
+
Fmt[:code] = Fmt.new.prec(20) # don't use :exact to avoid repeating numerals
|
1191
|
+
|
1192
|
+
class Fmt
|
1193
|
+
# Intermediate conversion format for simplified conversion
|
1194
|
+
CONV_FMT = Fmt.prec(:exact).rep('<','>','...',0).approx_mode(:simplify)
|
1195
|
+
# Intermediate conversion format for exact conversion
|
1196
|
+
CONV_FMT_STRICT = Fmt.prec(:exact).rep('<','>','...',0).approx_mode(:exact)
|
1197
|
+
# Numerical conversion: converts the quantity +x+ to an object
|
1198
|
+
# of class +type+.
|
1199
|
+
#
|
1200
|
+
# The third parameter is the kind of conversion:
|
1201
|
+
# [<tt>:approx</tt>]
|
1202
|
+
# Tries to find an approximate simpler value if possible for inexact
|
1203
|
+
# numeric types. This is the default. This is slower in general and
|
1204
|
+
# may take some seconds in some cases.
|
1205
|
+
# [<tt>:exact</tt>]
|
1206
|
+
# Performs a conversion as exact as possible.
|
1207
|
+
# The third parameter is true for approximate
|
1208
|
+
# conversion (inexact values are simplified if possible) and false
|
1209
|
+
# for conversions as exact as possible.
|
1210
|
+
def Fmt.convert(x, type, mode=:approx)
|
1211
|
+
fmt = mode==:approx ? CONV_FMT : CONV_FMT_STRICT
|
1212
|
+
# return x.prec(type)
|
1213
|
+
if !(x.is_a?(type))
|
1214
|
+
# return type.nio_read(x.nio_write(fmt),fmt)
|
1215
|
+
|
1216
|
+
x = x.nio_write_neutral(fmt)
|
1217
|
+
x = type.nio_read_neutral(x)
|
1218
|
+
|
1219
|
+
end
|
1220
|
+
x
|
1221
|
+
end
|
1222
|
+
end
|
1223
|
+
|
1224
|
+
module_function
|
1225
|
+
|
1226
|
+
def nio_float_to_bigdecimal(x,prec) # :nodoc:
|
1227
|
+
if prec.nil?
|
1228
|
+
x = Nio.convert(x,BigDecimal,:approx)
|
1229
|
+
elsif prec==:exact
|
1230
|
+
x = Nio.convert(x,BigDecimal,:exact)
|
1231
|
+
else
|
1232
|
+
x = BigDecimal(x.nio_write(Nio::Fmt.new.prec(prec,:sig)))
|
1233
|
+
end
|
1234
|
+
x
|
1235
|
+
end
|
1236
|
+
|
1237
|
+
|
1238
|
+
module Clinger # :nodoc: all
|
1239
|
+
module_function
|
1240
|
+
|
1241
|
+
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)
|
1242
|
+
|
1243
|
+
if e<0
|
1244
|
+
u,v,k = f,eb**(-e),0
|
1245
|
+
else
|
1246
|
+
u,v,k = f*(eb**e),1,0
|
1247
|
+
end
|
1248
|
+
|
1249
|
+
loop do
|
1250
|
+
x = u.div(v)
|
1251
|
+
# overflow if k>=max_e
|
1252
|
+
if (x>=beta**(n-1) && x<beta**n) || k==min_e || k==max_e
|
1253
|
+
return ratio_float(u,v,k,round_mode,beta,n)
|
1254
|
+
elsif x<beta**(n-1)
|
1255
|
+
u *= beta
|
1256
|
+
k -= 1
|
1257
|
+
elsif x>=beta**n
|
1258
|
+
v *= beta
|
1259
|
+
k += 1
|
1260
|
+
end
|
1261
|
+
end
|
1262
|
+
|
1263
|
+
end
|
1264
|
+
|
1265
|
+
def ratio_float(u,v,k,round_mode,beta=Float::RADIX,n=Float::MANT_DIG)
|
1266
|
+
q = u.div v
|
1267
|
+
r = u-q*v
|
1268
|
+
v_r = v-r
|
1269
|
+
z = Math.ldexp(q,k)
|
1270
|
+
if r<v_r
|
1271
|
+
z
|
1272
|
+
elsif r>v_r
|
1273
|
+
nextfloat z
|
1274
|
+
elsif (round_mode==:even && q.even?) || (round_mode==:zero)
|
1275
|
+
z
|
1276
|
+
else
|
1277
|
+
nextfloat z
|
1278
|
+
end
|
1279
|
+
end
|
1280
|
+
|
1281
|
+
def nextfloat(x)
|
1282
|
+
f,e = Math.frexp(x)
|
1283
|
+
e = Float::MIN_EXP if f==0
|
1284
|
+
e = [Float::MIN_EXP,e].max
|
1285
|
+
dx = Math.ldexp(1,e-Float::MANT_DIG) #Math.ldexp(Math.ldexp(1.0,-Float::MANT_DIG),e)
|
1286
|
+
x + dx
|
1287
|
+
end
|
1288
|
+
|
1289
|
+
def prevfloat(x)
|
1290
|
+
f,e = Math.frexp(x)
|
1291
|
+
e = Float::MIN_EXP if f==0
|
1292
|
+
e = [Float::MIN_EXP,e].max
|
1293
|
+
dx = Math.ldexp(1,e-Float::MANT_DIG) #Math.ldexp(Math.ldexp(1.0,-Float::MANT_DIG),e)
|
1294
|
+
if e==Float::MIN_EXP || f!=0.5 #0.5==Math.ldexp(2**(bits-1),-Float::MANT_DIG)
|
1295
|
+
x - dx
|
1296
|
+
else
|
1297
|
+
x - dx/2 # x - Math.ldexp(Math.ldexp(1.0,-Float::MANT_DIG),e-1)
|
1298
|
+
end
|
1299
|
+
end
|
1300
|
+
|
1301
|
+
end
|
1302
|
+
|
1303
|
+
module BurgerDybvig # :nodoc: all
|
1304
|
+
module_function
|
1305
|
+
|
1306
|
+
def float_to_digits(v,f,e,round_mode,min_e,p,b,_B)
|
1307
|
+
|
1308
|
+
case round_mode
|
1309
|
+
when :even
|
1310
|
+
roundl = roundh = f.even?
|
1311
|
+
when :inf
|
1312
|
+
roundl = true
|
1313
|
+
roundh = false
|
1314
|
+
when :zero
|
1315
|
+
roundl = false
|
1316
|
+
roundh = true
|
1317
|
+
else
|
1318
|
+
# here we don't assume any rounding in the floating point numbers
|
1319
|
+
# the result is valid for any rounding
|
1320
|
+
roundl = false
|
1321
|
+
roundh = false
|
1322
|
+
end
|
1323
|
+
|
1324
|
+
if e >= 0
|
1325
|
+
if f != exptt(b,p-1)
|
1326
|
+
be = exptt(b,e)
|
1327
|
+
r,s,m_p,m_m,k = scale(f*be*2,2,be,be,0,_B,roundl ,roundh,v)
|
1328
|
+
else
|
1329
|
+
be = exptt(b,e)
|
1330
|
+
be1 = be*b
|
1331
|
+
r,s,m_p,m_m,k = scale(f*be1*2,b*2,be1,be,0,_B,roundl ,roundh,v)
|
1332
|
+
end
|
1333
|
+
else
|
1334
|
+
if e==min_e or f != exptt(b,p-1)
|
1335
|
+
r,s,m_p,m_m,k = scale(f*2,exptt(b,-e)*2,1,1,0,_B,roundl ,roundh,v)
|
1336
|
+
else
|
1337
|
+
r,s,m_p,m_m,k = scale(f*b*2,exptt(b,1-e)*2,b,1,0,_B,roundl ,roundh,v)
|
1338
|
+
end
|
1339
|
+
end
|
1340
|
+
[k]+generate(r,s,m_p,m_m,_B,roundl ,roundh)
|
1341
|
+
end
|
1342
|
+
|
1343
|
+
def scale(r,s,m_p,m_m,k,_B,low_ok ,high_ok,v)
|
1344
|
+
return scale2(r,s,m_p,m_m,k,_B,low_ok ,high_ok) if v==0
|
1345
|
+
est = (logB(_B,v)-1E-10).ceil.to_i
|
1346
|
+
if est>=0
|
1347
|
+
fixup(r,s*exptt(_B,est),m_p,m_m,est,_B,low_ok,high_ok)
|
1348
|
+
else
|
1349
|
+
sc = exptt(_B,-est)
|
1350
|
+
fixup(r*sc,s,m_p*sc,m_m*sc,est,_B,low_ok,high_ok)
|
1351
|
+
end
|
1352
|
+
end
|
1353
|
+
|
1354
|
+
def fixup(r,s,m_p,m_m,k,_B,low_ok,high_ok)
|
1355
|
+
if (high_ok ? (r+m_p >= s) : (r+m_p > s)) # too low?
|
1356
|
+
[r,s*_B,m_p,m_m,k+1]
|
1357
|
+
else
|
1358
|
+
[r,s,m_p,m_m,k]
|
1359
|
+
end
|
1360
|
+
end
|
1361
|
+
|
1362
|
+
def scale2(r,s,m_p,m_m,k,_B,low_ok ,high_ok)
|
1363
|
+
loop do
|
1364
|
+
if (high_ok ? (r+m_p >= s) : (r+m_p > s)) # k is too low
|
1365
|
+
s *= _B
|
1366
|
+
k += 1
|
1367
|
+
elsif (high_ok ? ((r+m_p)*_B<s) : ((r+m_p)*_B<=s)) # k is too high
|
1368
|
+
r *= _B
|
1369
|
+
m_p *= _B
|
1370
|
+
m_m *= _B
|
1371
|
+
k -= 1
|
1372
|
+
else
|
1373
|
+
break
|
1374
|
+
end
|
1375
|
+
end
|
1376
|
+
[r,s,m_p,m_m,k]
|
1377
|
+
end
|
1378
|
+
|
1379
|
+
def generate(r,s,m_p,m_m,_B,low_ok ,high_ok)
|
1380
|
+
list = []
|
1381
|
+
loop do
|
1382
|
+
d,r = (r*_B).divmod(s)
|
1383
|
+
m_p *= _B
|
1384
|
+
m_m *= _B
|
1385
|
+
tc1 = low_ok ? (r<=m_m) : (r<m_m)
|
1386
|
+
tc2 = high_ok ? (r+m_p >= s) : (r+m_p > s)
|
1387
|
+
|
1388
|
+
if not tc1
|
1389
|
+
if not tc2
|
1390
|
+
list << d
|
1391
|
+
else
|
1392
|
+
list << d+1
|
1393
|
+
break
|
1394
|
+
end
|
1395
|
+
else
|
1396
|
+
if not tc2
|
1397
|
+
list << d
|
1398
|
+
break
|
1399
|
+
else
|
1400
|
+
if r*2 < s
|
1401
|
+
list << d
|
1402
|
+
break
|
1403
|
+
else
|
1404
|
+
list << d+1
|
1405
|
+
break
|
1406
|
+
end
|
1407
|
+
end
|
1408
|
+
end
|
1409
|
+
|
1410
|
+
end
|
1411
|
+
list
|
1412
|
+
end
|
1413
|
+
|
1414
|
+
$exptt_table = Array.new(326)
|
1415
|
+
(0...326).each{|i| $exptt_table[i]=10**i}
|
1416
|
+
def exptt(_B, k)
|
1417
|
+
if _B==10 && k>=0 && k<326
|
1418
|
+
$exptt_table[k]
|
1419
|
+
else
|
1420
|
+
_B**k
|
1421
|
+
end
|
1422
|
+
end
|
1423
|
+
|
1424
|
+
$logB_table = Array.new(37)
|
1425
|
+
(2...37).each{|b| $logB_table[b]=1.0/Math.log(b)}
|
1426
|
+
def logB(_B, x)
|
1427
|
+
if _B>=2 && _B<37
|
1428
|
+
Math.log(x)*$logB_table[_B]
|
1429
|
+
else
|
1430
|
+
Math.log(x)/Math.log(_B)
|
1431
|
+
end
|
1432
|
+
end
|
1433
|
+
|
1434
|
+
def float_to_digits_max(v,f,e,round_mode,min_e,p,b,_B)
|
1435
|
+
|
1436
|
+
case round_mode
|
1437
|
+
when :even
|
1438
|
+
roundl = roundh = f.even?
|
1439
|
+
when :inf
|
1440
|
+
roundl = true
|
1441
|
+
roundh = false
|
1442
|
+
when :zero
|
1443
|
+
roundl = false
|
1444
|
+
roundh = true
|
1445
|
+
else
|
1446
|
+
# here we don't assume any rounding in the floating point numbers
|
1447
|
+
# the result is valid for any rounding
|
1448
|
+
roundl = false
|
1449
|
+
roundh = false
|
1450
|
+
end
|
1451
|
+
|
1452
|
+
if e >= 0
|
1453
|
+
if f != exptt(b,p-1)
|
1454
|
+
be = exptt(b,e)
|
1455
|
+
r,s,m_p,m_m,k = scale(f*be*2,2,be,be,0,_B,roundl ,roundh,v)
|
1456
|
+
else
|
1457
|
+
be = exptt(b,e)
|
1458
|
+
be1 = be*b
|
1459
|
+
r,s,m_p,m_m,k = scale(f*be1*2,b*2,be1,be,0,_B,roundl ,roundh,v)
|
1460
|
+
end
|
1461
|
+
else
|
1462
|
+
if e==min_e or f != exptt(b,p-1)
|
1463
|
+
r,s,m_p,m_m,k = scale(f*2,exptt(b,-e)*2,1,1,0,_B,roundl ,roundh,v)
|
1464
|
+
else
|
1465
|
+
r,s,m_p,m_m,k = scale(f*b*2,exptt(b,1-e)*2,b,1,0,_B,roundl ,roundh,v)
|
1466
|
+
end
|
1467
|
+
end
|
1468
|
+
[k]+generate_max(r,s,m_p,m_m,_B,roundl ,roundh)
|
1469
|
+
end
|
1470
|
+
|
1471
|
+
def generate_max(r,s,m_p,m_m,_B,low_ok ,high_ok)
|
1472
|
+
list = [false]
|
1473
|
+
loop do
|
1474
|
+
d,r = (r*_B).divmod(s)
|
1475
|
+
m_p *= _B
|
1476
|
+
m_m *= _B
|
1477
|
+
|
1478
|
+
list << d
|
1479
|
+
|
1480
|
+
tc1 = low_ok ? (r<=m_m) : (r<m_m)
|
1481
|
+
tc2 = high_ok ? (r+m_p >= s) : (r+m_p > s)
|
1482
|
+
|
1483
|
+
if tc1 && tc2
|
1484
|
+
list[0] = true if r*2 >= s
|
1485
|
+
break
|
1486
|
+
end
|
1487
|
+
end
|
1488
|
+
list
|
1489
|
+
end
|
1490
|
+
|
1491
|
+
end
|
1492
|
+
|
1493
|
+
end
|
1494
|
+
|
1495
|
+
class Float
|
1496
|
+
include Nio::Formattable
|
1497
|
+
def self.nio_read_neutral(neutral)
|
1498
|
+
x = nil
|
1499
|
+
|
1500
|
+
honor_rounding = true
|
1501
|
+
|
1502
|
+
if neutral.special?
|
1503
|
+
case neutral.special
|
1504
|
+
when :nan
|
1505
|
+
x = 0.0/0.0
|
1506
|
+
when :inf
|
1507
|
+
x = (neutral.sign=='-' ? -1.0 : +1.0)/0.0
|
1508
|
+
end
|
1509
|
+
elsif neutral.rep_pos<neutral.digits.length
|
1510
|
+
|
1511
|
+
x,y = neutral.to_RepDec.getQ
|
1512
|
+
x = Float(x)/y
|
1513
|
+
|
1514
|
+
else
|
1515
|
+
nd = neutral.base==10 ? Float::DIG : ((Float::MANT_DIG-1)*Math.log(2)/Math.log(neutral.base)).floor
|
1516
|
+
k = neutral.dec_pos-neutral.digits.length
|
1517
|
+
if !honor_rounding && (neutral.digits.length<=nd && k.abs<=15)
|
1518
|
+
x = neutral.digits.to_i(neutral.base).to_f
|
1519
|
+
if k<0
|
1520
|
+
x /= Float(neutral.base**-k)
|
1521
|
+
else
|
1522
|
+
x *= Float(neutral.base**k)
|
1523
|
+
end
|
1524
|
+
x = -x if neutral.sign=='-'
|
1525
|
+
elsif !honor_rounding && (k>0 && (k+neutral.digits.length < 2*nd))
|
1526
|
+
j = k-neutral.digits.length
|
1527
|
+
x = neutral.digits.to_i(neutral.base).to_f * Float(neutral.base**(j))
|
1528
|
+
x *= Float(neutral.base**(k-j))
|
1529
|
+
x = -x if neutral.sign=='-'
|
1530
|
+
elsif neutral.base.modulo(Float::RADIX)==0
|
1531
|
+
|
1532
|
+
f = neutral.digits.to_i(neutral.base)
|
1533
|
+
e = neutral.dec_pos-neutral.digits.length
|
1534
|
+
|
1535
|
+
rounding = neutral.rounding
|
1536
|
+
|
1537
|
+
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)
|
1538
|
+
x = -x if neutral.sign=='-'
|
1539
|
+
|
1540
|
+
else
|
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
|
+
end
|
1551
|
+
end
|
1552
|
+
|
1553
|
+
return x
|
1554
|
+
end
|
1555
|
+
def nio_write_neutral(fmt)
|
1556
|
+
neutral = Nio::NeutralNum.new
|
1557
|
+
x = self
|
1558
|
+
|
1559
|
+
if x.nan?
|
1560
|
+
neutral.set_special(:nan)
|
1561
|
+
elsif x.infinite?
|
1562
|
+
neutral.set_special(:inf, x<0 ? '-' : '+')
|
1563
|
+
else
|
1564
|
+
converted = false
|
1565
|
+
if fmt.get_ndig==:exact && fmt.get_approx==:simplify
|
1566
|
+
|
1567
|
+
if x!=0
|
1568
|
+
q = x.nio_r(Nio::Tolerance.decimals(Float::DIG,:sig))
|
1569
|
+
if q!=0
|
1570
|
+
neutral = q.nio_write_neutral(fmt)
|
1571
|
+
converted = true if neutral.digits.length<=Float::DIG
|
1572
|
+
end
|
1573
|
+
end
|
1574
|
+
|
1575
|
+
elsif fmt.get_approx==:exact
|
1576
|
+
neutral = x.nio_xr.nio_write_neutral(fmt)
|
1577
|
+
converted = true
|
1578
|
+
end
|
1579
|
+
if !converted
|
1580
|
+
if fmt.get_base==10 && false
|
1581
|
+
txt = format "%.*e",Float::DECIMAL_DIG-1,x # note that spec. e output precision+1 significant digits
|
1582
|
+
|
1583
|
+
sign = '+'
|
1584
|
+
if txt[0,1]=='-'
|
1585
|
+
sign = '-'
|
1586
|
+
txt = txt[1...txt.length]
|
1587
|
+
end
|
1588
|
+
exp = 0
|
1589
|
+
x_char = fmt.get_exp_char(fmt.get_base)
|
1590
|
+
|
1591
|
+
exp_i = txt.index(x_char)
|
1592
|
+
exp_i = txt.index(x_char.downcase) if exp_i===nil
|
1593
|
+
if exp_i!=nil
|
1594
|
+
exp = txt[exp_i+1...txt.length].to_i
|
1595
|
+
txt = txt[0...exp_i]
|
1596
|
+
end
|
1597
|
+
|
1598
|
+
dec_pos = txt.index '.'
|
1599
|
+
if dec_pos==nil
|
1600
|
+
dec_pos = txt.length
|
1601
|
+
else
|
1602
|
+
txt[dec_pos]=''
|
1603
|
+
end
|
1604
|
+
dec_pos += exp
|
1605
|
+
neutral.set sign, txt, dec_pos, nil, fmt.get_base_digits(10), true, fmt.get_round
|
1606
|
+
|
1607
|
+
converted = true
|
1608
|
+
end
|
1609
|
+
end
|
1610
|
+
if !converted
|
1611
|
+
|
1612
|
+
sign = x<0 ? '-' : '+'
|
1613
|
+
x = -x if sign=='-'
|
1614
|
+
f,e = Math.frexp(x)
|
1615
|
+
if e < Float::MIN_EXP
|
1616
|
+
# denormalized number
|
1617
|
+
f = Math.ldexp(f,e-Float::MIN_EXP+Float::MANT_DIG)
|
1618
|
+
e = Float::MIN_EXP-Float::MANT_DIG
|
1619
|
+
else
|
1620
|
+
# normalized number
|
1621
|
+
f = Math.ldexp(f,Float::MANT_DIG)
|
1622
|
+
e -= Float::MANT_DIG
|
1623
|
+
end
|
1624
|
+
f = f.to_i
|
1625
|
+
inexact = true
|
1626
|
+
|
1627
|
+
rounding = fmt.get_round
|
1628
|
+
|
1629
|
+
if fmt.get_all_digits?
|
1630
|
+
# use as many digits as possible
|
1631
|
+
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)
|
1632
|
+
inexact = :roundup if r
|
1633
|
+
else
|
1634
|
+
# use as few digits as possible
|
1635
|
+
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)
|
1636
|
+
end
|
1637
|
+
txt = ''
|
1638
|
+
digits.each{|d| txt << fmt.get_base_digits.digit_char(d)}
|
1639
|
+
neutral.set sign, txt, dec_pos, nil, fmt.get_base_digits, inexact, fmt.get_round
|
1640
|
+
|
1641
|
+
end
|
1642
|
+
end
|
1643
|
+
|
1644
|
+
return neutral
|
1645
|
+
end
|
1646
|
+
end
|
1647
|
+
|
1648
|
+
class Numeric
|
1649
|
+
unless method_defined?(:even?)
|
1650
|
+
def even?
|
1651
|
+
self.modulo(2)==0
|
1652
|
+
end
|
1653
|
+
end
|
1654
|
+
unless method_defined?(:odd?)
|
1655
|
+
def odd?
|
1656
|
+
self.modulo(2)!=0
|
1657
|
+
end
|
1658
|
+
end
|
1659
|
+
end
|
1660
|
+
|
1661
|
+
class Integer
|
1662
|
+
include Nio::Formattable
|
1663
|
+
def self.nio_read_neutral(neutral)
|
1664
|
+
x = nil
|
1665
|
+
|
1666
|
+
if neutral.special?
|
1667
|
+
raise Nio::InvalidFormat,"Invalid integer numeral"
|
1668
|
+
elsif neutral.rep_pos<neutral.digits.length
|
1669
|
+
return Rational.nio_read_neutral(neutral).to_i
|
1670
|
+
else
|
1671
|
+
digits = neutral.digits
|
1672
|
+
|
1673
|
+
if neutral.dec_pos <= 0
|
1674
|
+
digits = '0'
|
1675
|
+
elsif neutral.dec_pos <= digits.length
|
1676
|
+
digits = digits[0...neutral.dec_pos]
|
1677
|
+
else
|
1678
|
+
digits = digits + '0'*(neutral.dec_pos-digits.length)
|
1679
|
+
end
|
1680
|
+
|
1681
|
+
x = digits.to_i(neutral.base)
|
1682
|
+
# this was formely needed because we didn't adust the digits
|
1683
|
+
# if neutral.dec_pos != neutral.digits.length
|
1684
|
+
# # with rational included, negative powers of ten are rational numbers
|
1685
|
+
# x = (x*((neutral.base)**(neutral.dec_pos-neutral.digits.length))).to_i
|
1686
|
+
# end
|
1687
|
+
x = -x if neutral.sign=='-'
|
1688
|
+
end
|
1689
|
+
|
1690
|
+
return x
|
1691
|
+
end
|
1692
|
+
def nio_write_neutral(fmt)
|
1693
|
+
neutral = Nio::NeutralNum.new
|
1694
|
+
x = self
|
1695
|
+
|
1696
|
+
sign = x<0 ? '-' : '+'
|
1697
|
+
txt = x.abs.to_s(fmt.get_base)
|
1698
|
+
dec_pos = rep_pos = txt.length
|
1699
|
+
neutral.set sign, txt, dec_pos, nil, fmt.get_base_digits, false ,fmt.get_round
|
1700
|
+
|
1701
|
+
return neutral
|
1702
|
+
end
|
1703
|
+
end
|
1704
|
+
|
1705
|
+
class Rational
|
1706
|
+
include Nio::Formattable
|
1707
|
+
def self.nio_read_neutral(neutral)
|
1708
|
+
x = nil
|
1709
|
+
|
1710
|
+
if neutral.special?
|
1711
|
+
case neutral.special
|
1712
|
+
when :nan
|
1713
|
+
x = Rational(0,0)
|
1714
|
+
when :inf
|
1715
|
+
x = Rational((neutral.sign=='-' ? -1 : +1),0)
|
1716
|
+
end
|
1717
|
+
else
|
1718
|
+
x = Rational(*neutral.to_RepDec.getQ)
|
1719
|
+
end
|
1720
|
+
|
1721
|
+
return x
|
1722
|
+
end
|
1723
|
+
def nio_write_neutral(fmt)
|
1724
|
+
neutral = Nio::NeutralNum.new
|
1725
|
+
x = self
|
1726
|
+
|
1727
|
+
if x.denominator==0
|
1728
|
+
if x.numerator>0
|
1729
|
+
neutral.set_special(:inf)
|
1730
|
+
elsif x.numerator<0
|
1731
|
+
neutral.set_special(:inf,'-')
|
1732
|
+
else
|
1733
|
+
neutral.set_special(:nan)
|
1734
|
+
end
|
1735
|
+
else
|
1736
|
+
if fmt.get_base==10
|
1737
|
+
rd = Nio::RepDec.new.setQ(x.numerator,x.denominator)
|
1738
|
+
else
|
1739
|
+
opt = Nio::RepDec::DEF_OPT.dup.set_digits(fmt.get_base_digits)
|
1740
|
+
rd = Nio::RepDec.new.setQ(x.numerator,x.denominator, opt)
|
1741
|
+
end
|
1742
|
+
neutral = rd.to_NeutralNum(fmt.get_base_digits)
|
1743
|
+
neutral.rounding = fmt.get_round
|
1744
|
+
end
|
1745
|
+
|
1746
|
+
return neutral
|
1747
|
+
end
|
1748
|
+
end
|
1749
|
+
|
1750
|
+
if defined? BigDecimal
|
1751
|
+
class BigDecimal
|
1752
|
+
include Nio::Formattable
|
1753
|
+
def self.nio_read_neutral(neutral)
|
1754
|
+
x = nil
|
1755
|
+
|
1756
|
+
if neutral.special?
|
1757
|
+
case neutral.special
|
1758
|
+
when :nan
|
1759
|
+
x = BigDecimal('NaN') # BigDecimal("0")/0
|
1760
|
+
when :inf
|
1761
|
+
x = BigDecimal(neutral.sign=='-' ? '-1.0' : '+1.0')/0
|
1762
|
+
end
|
1763
|
+
elsif neutral.rep_pos<neutral.digits.length
|
1764
|
+
|
1765
|
+
x,y = neutral.to_RepDec.getQ
|
1766
|
+
x = BigDecimal(x.to_s)/y
|
1767
|
+
|
1768
|
+
else
|
1769
|
+
if neutral.base==10
|
1770
|
+
#x = BigDecimal(neutral.digits)
|
1771
|
+
#x *= BigDecimal("1E#{(neutral.dec_pos-neutral.digits.length)}")
|
1772
|
+
#x = -x if neutral.sign=='-'
|
1773
|
+
str = neutral.sign
|
1774
|
+
str += neutral.digits
|
1775
|
+
str += "E#{(neutral.dec_pos-neutral.digits.length)}"
|
1776
|
+
x = BigDecimal(str)
|
1777
|
+
else
|
1778
|
+
x = BigDecimal(neutral.digits.to_i(neutral.base).to_s)
|
1779
|
+
x *= BigDecimal(neutral.base.to_s)**(neutral.dec_pos-neutral.digits.length)
|
1780
|
+
x = -x if neutral.sign=='-'
|
1781
|
+
end
|
1782
|
+
end
|
1783
|
+
|
1784
|
+
return x
|
1785
|
+
end
|
1786
|
+
def nio_write_neutral(fmt)
|
1787
|
+
neutral = Nio::NeutralNum.new
|
1788
|
+
x = self
|
1789
|
+
|
1790
|
+
if x.nan?
|
1791
|
+
neutral.set_special(:nan)
|
1792
|
+
elsif x.infinite?
|
1793
|
+
neutral.set_special(:inf, x<0 ? '-' : '+')
|
1794
|
+
else
|
1795
|
+
converted = false
|
1796
|
+
if fmt.get_ndig==:exact && fmt.get_approx==:simplify
|
1797
|
+
|
1798
|
+
prc = [x.precs[0],20].max
|
1799
|
+
neutral = x.nio_r(Nio::BigTolerance.decimals(prc,:sig)).nio_write_neutral(fmt)
|
1800
|
+
converted = true if neutral.digits.length<prc
|
1801
|
+
|
1802
|
+
elsif fmt.get_approx==:exact && fmt.get_base!=10
|
1803
|
+
neutral = x.nio_xr.nio_write_neutral(fmt)
|
1804
|
+
converted = true
|
1805
|
+
end
|
1806
|
+
if !converted
|
1807
|
+
if fmt.get_base==10
|
1808
|
+
txt = x.to_s
|
1809
|
+
|
1810
|
+
sign = '+'
|
1811
|
+
if txt[0,1]=='-'
|
1812
|
+
sign = '-'
|
1813
|
+
txt = txt[1...txt.length]
|
1814
|
+
end
|
1815
|
+
exp = 0
|
1816
|
+
x_char = fmt.get_exp_char(fmt.get_base)
|
1817
|
+
|
1818
|
+
exp_i = txt.index(x_char)
|
1819
|
+
exp_i = txt.index(x_char.downcase) if exp_i===nil
|
1820
|
+
if exp_i!=nil
|
1821
|
+
exp = txt[exp_i+1...txt.length].to_i
|
1822
|
+
txt = txt[0...exp_i]
|
1823
|
+
end
|
1824
|
+
|
1825
|
+
dec_pos = txt.index '.'
|
1826
|
+
if dec_pos==nil
|
1827
|
+
dec_pos = txt.length
|
1828
|
+
else
|
1829
|
+
txt[dec_pos]=''
|
1830
|
+
end
|
1831
|
+
dec_pos += exp
|
1832
|
+
neutral.set sign, txt, dec_pos, nil, fmt.get_base_digits(10), true, fmt.get_round
|
1833
|
+
|
1834
|
+
end
|
1835
|
+
end
|
1836
|
+
if !converted
|
1837
|
+
|
1838
|
+
min_prec = 24
|
1839
|
+
min_exp = -1000
|
1840
|
+
s,f,b,e = x.split
|
1841
|
+
e -= f.size
|
1842
|
+
sign = s<0 ? '-' : '+'
|
1843
|
+
x = -x if sign=='-'
|
1844
|
+
f_i = f.to_i
|
1845
|
+
prc = [x.precs[0],min_prec].max
|
1846
|
+
f_i *= 10**(prc-f.size)
|
1847
|
+
e -= (prc-f.size)
|
1848
|
+
|
1849
|
+
inexact = true
|
1850
|
+
|
1851
|
+
rounding = fmt.get_round
|
1852
|
+
|
1853
|
+
if fmt.get_all_digits?
|
1854
|
+
# use as many digits as possible
|
1855
|
+
dec_pos,r,*digits = Nio::BurgerDybvig::float_to_digits_max(x,f_i,e,rounding,[e,min_exp].min,prc,b,fmt.get_base)
|
1856
|
+
inexact = :roundup if r
|
1857
|
+
else
|
1858
|
+
# use as few digits as possible
|
1859
|
+
dec_pos,*digits = Nio::BurgerDybvig::float_to_digits(x,f_i,e,rounding,[e,min_exp].min,prc,b,fmt.get_base)
|
1860
|
+
end
|
1861
|
+
txt = ''
|
1862
|
+
digits.each{|d| txt << fmt.get_base_digits.digit_char(d)}
|
1863
|
+
neutral.set sign, txt, dec_pos, nil, fmt.get_base_digits, inexact, fmt.get_round
|
1864
|
+
|
1865
|
+
end
|
1866
|
+
end
|
1867
|
+
|
1868
|
+
return neutral
|
1869
|
+
end
|
1870
|
+
end
|
1871
|
+
end
|
1872
|
+
|