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