flt 1.5.0 → 1.5.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/History.txt +8 -0
  3. data/README.md +765 -0
  4. data/expand.rb +2 -0
  5. data/flt.gemspec +1 -0
  6. data/lib/flt.rb +1 -1
  7. data/lib/flt/bigdecimal.rb +2 -5
  8. data/lib/flt/bin_num.rb +1 -4
  9. data/lib/flt/complex.rb +10 -9
  10. data/lib/flt/dec_num.rb +9 -10
  11. data/lib/flt/float.rb +4 -6
  12. data/lib/flt/math.rb +3 -4
  13. data/lib/flt/num.rb +54 -21
  14. data/lib/flt/sugar.rb +1 -2
  15. data/lib/flt/support.rb +5 -2
  16. data/lib/flt/support/flag_values.rb +1 -1
  17. data/lib/flt/support/rationalizer.rb +3 -3
  18. data/lib/flt/support/rationalizer_extra.rb +6 -6
  19. data/lib/flt/support/reader.rb +1 -1
  20. data/lib/flt/tolerance.rb +2 -7
  21. data/lib/flt/trigonometry.rb +0 -2
  22. data/lib/flt/version.rb +1 -1
  23. data/setup.rb +1 -2
  24. data/test/generate_trig_data.rb +6 -6
  25. data/test/helper.rb +11 -5
  26. data/test/reader.rb +1 -1
  27. data/test/test_base_digits.rb +4 -2
  28. data/test/test_basic.rb +7 -7
  29. data/test/test_big_decimal.rb +21 -21
  30. data/test/test_bin.rb +1 -1
  31. data/test/test_bin_arithmetic.rb +1 -1
  32. data/test/test_binfloat_conversion.rb +1 -1
  33. data/test/test_coercion.rb +1 -1
  34. data/test/test_comparisons.rb +4 -4
  35. data/test/test_dectest.rb +2 -2
  36. data/test/test_define_conversions.rb +10 -10
  37. data/test/test_epsilon.rb +1 -1
  38. data/test/test_exact.rb +12 -11
  39. data/test/test_flags.rb +1 -1
  40. data/test/test_float.rb +23 -25
  41. data/test/test_format.rb +1 -1
  42. data/test/test_formatter.rb +6 -8
  43. data/test/test_hex_format.rb +2 -2
  44. data/test/test_multithreading.rb +1 -1
  45. data/test/test_normalized.rb +1 -1
  46. data/test/test_num_constructor.rb +6 -1
  47. data/test/test_odd_even.rb +1 -1
  48. data/test/test_rationalizer.rb +1 -1
  49. data/test/test_round.rb +36 -1
  50. data/test/test_sugar.rb +6 -6
  51. data/test/test_to_int.rb +4 -4
  52. data/test/test_to_rf.rb +126 -1
  53. data/test/test_tol.rb +1 -1
  54. data/test/test_trig.rb +1 -1
  55. data/test/test_ulp.rb +3 -3
  56. metadata +18 -4
  57. data/README.rdoc +0 -614
data/expand.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # TODO: adapt for Github-Flavored-Markdown
2
+
1
3
  require 'irb/ruby-lex'
2
4
  require 'stringio'
3
5
 
@@ -20,4 +20,5 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  spec.add_development_dependency "bundler", "~> 1.6"
22
22
  spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "minitest", "~> 5.10"
23
24
  end
data/lib/flt.rb CHANGED
@@ -6,9 +6,9 @@ require 'flt/support/flag_values'
6
6
  require 'flt/support/reader'
7
7
  require 'flt/support/formatter'
8
8
  require 'flt/support/rationalizer'
9
+ require 'flt/num'
9
10
  require 'flt/float'
10
11
  require 'flt/bigdecimal'
11
- require 'flt/num'
12
12
  require 'flt/dec_num'
13
13
  require 'flt/bin_num'
14
14
  require 'flt/tolerance'
@@ -1,8 +1,5 @@
1
1
  # Support classes for homogeneous treatment of BigDecimal and Num values by defining BigDecimal.context
2
2
 
3
- require 'flt/num'
4
- require 'flt/dec_num'
5
-
6
3
  require 'bigdecimal'
7
4
  require 'bigdecimal/math'
8
5
  require 'singleton'
@@ -109,13 +106,13 @@ class Flt::BigDecimalContext
109
106
  end
110
107
 
111
108
  def split(x)
112
- sgn, d, b, e = x.split
109
+ sgn, d, _b, e = x.split
113
110
  [sgn<0 ? -1 : +1, d.to_i, e-d.size]
114
111
  end
115
112
 
116
113
  # Return the value of the number as an signed integer and a scale.
117
114
  def to_int_scale(x)
118
- sgn, d, b, e = x.split
115
+ sgn, d, _b, e = x.split
119
116
  c = d.to_i
120
117
  [sgn<0 ? -1 : c, -c, e-d.size]
121
118
  end
@@ -1,6 +1,3 @@
1
- require 'flt/num'
2
- require 'flt/float'
3
-
4
1
  module Flt
5
2
 
6
3
  class BinNum < Num
@@ -206,7 +203,7 @@ class BinNum < Num
206
203
  # Specific to_f conversion TODO: check if it represents an optimization
207
204
  if Float::RADIX==2
208
205
  def to_f
209
- if special?
206
+ if special? || zero?
210
207
  super
211
208
  else
212
209
  ::Math.ldexp(@sign*@coeff, @exp)
@@ -93,9 +93,9 @@ module Flt
93
93
  define_method mth do |*args|
94
94
  is_complex = args.detect{|z| z.kind_of?(Complex)}
95
95
  if is_complex || (negative_arg_to_complex && args.first<0)
96
- num_class.context(:extra_precision=>extra_prec) do |mth|
97
- #Complex.rectangular *blk[mth, *args].map{|v| @context.plus(v)}
98
- Complex.rectangular *instance_exec(mth,*args,&blk).map{|v| @context.plus(v)}
96
+ num_class.context(:extra_precision => extra_prec) do |extra_context|
97
+ #Complex.rectangular(*blk[extra_context, *args].map{|v| @context.plus(v)})
98
+ Complex.rectangular(*instance_exec(extra_context, *args, &blk).map{|v| @context.plus(v)})
99
99
  end
100
100
  else
101
101
  @context.send(mth, *args) # Num(@context.send(mth, *args)) ?
@@ -238,7 +238,7 @@ module Flt
238
238
  z_is_complex = z.kind_of?(Complex)
239
239
  if z_is_complex || z.abs>1
240
240
  # z = Complex(1) unless z_is_complex
241
- i = Complex(0,@context.num_class[1])
241
+ # i = Complex(0, @context.num_class[1])
242
242
  fix num_class.context(:extra_precision=>3).cmath{ num_class.one_half*ln((1+z)/(1-z)) }
243
243
  else
244
244
  @context.atanh(z)
@@ -256,6 +256,7 @@ module Flt
256
256
  end
257
257
 
258
258
  def fix_polar(r, theta)
259
+ re = im = nil
259
260
  num_class.context(:extra_precision=>3) do |mth|
260
261
  re = r*mth.cos(theta)
261
262
  im = r*mth.sin(theta)
@@ -264,7 +265,7 @@ module Flt
264
265
  end
265
266
 
266
267
  def fix(z)
267
- fix_rect *z.rectangular
268
+ fix_rect(*z.rectangular)
268
269
  end
269
270
 
270
271
  end # ComplexContext
@@ -280,9 +281,9 @@ module Flt
280
281
  # if ComplexContext is derived from ContextBase: return ComplexContext(self).math(*parameters, &blk)
281
282
  num_class.context(self) do
282
283
  if parameters.empty?
283
- Flt.ComplexContext(num_class.context).instance_eval &blk
284
+ Flt.ComplexContext(num_class.context).instance_eval(&blk)
284
285
  else
285
- Flt.xiComplexContext(num_class.context).instance_exec *parameters, &blk
286
+ Flt.xiComplexContext(num_class.context).instance_exec(*parameters, &blk)
286
287
  end
287
288
  end
288
289
  end
@@ -298,14 +299,14 @@ module Flt
298
299
  module DecNum::CMath
299
300
  include MathBase
300
301
  num_class(DecNum){num_class.ccontext}
301
- math_function *Trigonometry.public_instance_methods
302
+ math_function(*Trigonometry.public_instance_methods)
302
303
  math_function :exp, :log, :log2, :log10, :sqrt
303
304
  end
304
305
 
305
306
  module BinNum::CMath
306
307
  include MathBase
307
308
  num_class(BinNum){num_class.ccontext}
308
- math_function *Trigonometry.public_instance_methods
309
+ math_function(*Trigonometry.public_instance_methods)
309
310
  math_function :exp, :log, :log2, :log10, :sqrt
310
311
  end
311
312
 
@@ -1,6 +1,3 @@
1
- require 'flt/num'
2
- require 'flt/bigdecimal'
3
-
4
1
  module Flt
5
2
 
6
3
  # DecNum arbitrary precision floating point number.
@@ -1185,18 +1182,20 @@ class DecNum < Num
1185
1182
  return _div_nearest(_iexp(rem, 10**p), 1000), quot - p + 3
1186
1183
  end
1187
1184
 
1188
- # number of bits in a nonnegative integer
1185
+ # number of decimal digits in a nonnegative integer
1189
1186
  def _number_of_digits(i)
1190
1187
  raise TypeError, "The argument to _number_of_digits should be nonnegative." if i < 0
1191
- if i.is_a?(Fixnum) || (i > NUMBER_OF_DIGITS_MAX_VALID_LOG)
1192
- # for short integers this is faster
1193
- # note that here we return 1 for 0
1194
- i.to_s.length
1188
+ return 1 if i.zero?
1189
+ nb = _nbits(i)
1190
+ nd1 = ((nb-1)*LOG10_2).floor + 1
1191
+ nd2 = (nb*LOG10_2).floor + 1
1192
+ if nd1 == nd2
1193
+ nd1
1195
1194
  else
1196
- (::Math.log10(i)+1).floor
1195
+ i >= 10**nd1 ? nd2 : nd1
1197
1196
  end
1198
1197
  end
1199
- NUMBER_OF_DIGITS_MAX_VALID_LOG = 10**(Float::DIG-1)
1198
+ LOG10_2 = Math.log10(2)
1200
1199
 
1201
1200
  end # AuxiliarFunctions
1202
1201
 
@@ -2,10 +2,8 @@
2
2
  #
3
3
  # The set of constants with Float metadata is also augmented.
4
4
 
5
- require 'flt/num'
6
5
  require 'singleton'
7
6
 
8
-
9
7
  # Float constants.
10
8
  #
11
9
  # Note that this uses the "fractional significand" interpretation,
@@ -80,8 +78,8 @@ class Float
80
78
  # Minimum non zero positive denormal number == 0.0.next
81
79
  MIN_D = Math.ldexp(1,Float::MIN_EXP-Float::MANT_DIG);
82
80
 
83
- # Maximum significand == Math.ldexp(Math.ldexp(1,Float::MANT_DIG)-1,-Float::MANT_DIG)
84
- MAX_F = Math.frexp(Float::MAX)[0] == Math.ldexp(Math.ldexp(1,Float::MANT_DIG)-1,-Float::MANT_DIG)
81
+ # Maximum significand == Math.ldexp(Math.ldexp(1,Float::MANT_DIG)-1,-Float::MANT_DIG)
82
+ MAX_F = Math.frexp(Float::MAX)[0]
85
83
 
86
84
  end
87
85
 
@@ -491,10 +489,10 @@ class Flt::FloatContext
491
489
 
492
490
  def math(*parameters, &blk)
493
491
  if parameters.empty?
494
- self.instance_eval &blk
492
+ self.instance_eval(&blk)
495
493
  else
496
494
  # needs instance_exe (available in Ruby 1.9, ActiveRecord; TODO: include implementation here)
497
- self.instance_exec *parameters, &blk
495
+ self.instance_exec(*parameters, &blk)
498
496
  end
499
497
  end
500
498
 
@@ -1,5 +1,4 @@
1
- require 'flt/dec_num'
2
- require 'flt/bin_num'
1
+ require 'flt'
3
2
  require 'flt/trigonometry'
4
3
 
5
4
  module Flt
@@ -52,7 +51,7 @@ module Flt
52
51
  module DecNum::Math
53
52
  include MathBase
54
53
  num_class DecNum
55
- math_function *Trigonometry.public_instance_methods
54
+ math_function(*Trigonometry.public_instance_methods)
56
55
  math_function :exp, :log, :log2, :log10, :sqrt
57
56
  end
58
57
 
@@ -60,7 +59,7 @@ module Flt
60
59
  module BinNum::Math
61
60
  include MathBase
62
61
  num_class BinNum
63
- math_function *Trigonometry.public_instance_methods
62
+ math_function(*Trigonometry.public_instance_methods)
64
63
  math_function :exp, :log, :log2, :log10, :sqrt
65
64
  end
66
65
 
@@ -18,7 +18,6 @@ require 'flt/support/reader'
18
18
  require 'flt/support/formatter'
19
19
  require 'flt/support/rationalizer'
20
20
 
21
- require 'bigdecimal'
22
21
  require 'forwardable'
23
22
  require 'rational'
24
23
  require 'monitor'
@@ -216,7 +215,7 @@ class Num < Numeric
216
215
  class InvalidOperation < Exception
217
216
  def self.handle(context, *args)
218
217
  if args.size>0
219
- sign, coeff, exp = args.first.split
218
+ sign, coeff, _exp = args.first.split
220
219
  context.num_class.new([sign, coeff, :nan])._fix_nan(context)
221
220
  else
222
221
  context.num_class.nan
@@ -472,10 +471,10 @@ class Num < Numeric
472
471
  # TODO: consider renaming this to eval
473
472
  num_class.context(self) do
474
473
  if parameters.empty?
475
- num_class.context.instance_eval &blk
474
+ num_class.context.instance_eval(&blk)
476
475
  else
477
476
  # needs instance_exe (available in Ruby 1.9, ActiveRecord; TODO: include implementation here)
478
- num_class.context.instance_exec *parameters, &blk
477
+ num_class.context.instance_exec(*parameters, &blk)
479
478
  end
480
479
  end
481
480
  end
@@ -2418,7 +2417,7 @@ class Num < Numeric
2418
2417
  exp = self.exponent
2419
2418
  dgs = self.digits
2420
2419
  nd = dgs.size # self.number_of_digits
2421
- while dgs[nd-1]==0
2420
+ while dgs[nd-1]==0
2422
2421
  exp += 1
2423
2422
  nd -= 1
2424
2423
  end
@@ -2703,10 +2702,24 @@ class Num < Numeric
2703
2702
  else
2704
2703
  0.0/0.0
2705
2704
  end
2705
+ elsif zero?
2706
+ @sign*0.0
2706
2707
  else
2707
- # to_rational.to_f
2708
- # to_s.to_f
2709
- (@sign*@coeff*(num_class.radix.to_f**@exp)).to_f
2708
+ f = nil
2709
+ f ||= to_s.to_f if num_class.radix == 10 # very precise, but slow
2710
+ unless f
2711
+ c = @coeff.to_f
2712
+ if c.finite?
2713
+ # This is fast to compute but may introduce roundoff error, and overflow or yield NaN
2714
+ f = if @exp >= 0
2715
+ @sign*c*(num_class.radix.to_f**@exp)
2716
+ else
2717
+ @sign*c/(num_class.radix.to_f**(-@exp))
2718
+ end
2719
+ end
2720
+ f = nil unless f && f.finite?
2721
+ end
2722
+ f ||= to_r.to_f # not so slow, also may introduce roundoff error due to arithmetic
2710
2723
  end
2711
2724
  end
2712
2725
 
@@ -2884,8 +2897,14 @@ class Num < Numeric
2884
2897
  end
2885
2898
 
2886
2899
  # Digits of the significand as an array of integers
2887
- def digits
2888
- @coeff.to_s(num_class.radix).split('').map{|d| d.to_i} # TODO: optimize in derivided classes
2900
+ if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.4.0')
2901
+ def digits
2902
+ @coeff.is_a?(Integer) ? @coeff.digits(num_class.radix).reverse! : []
2903
+ end
2904
+ else
2905
+ def digits
2906
+ @coeff.to_s(num_class.radix).split('').map{|d| d.to_i}
2907
+ end
2889
2908
  end
2890
2909
 
2891
2910
  # Significand as an integer, unsigned. Synonym of coefficient
@@ -3124,21 +3143,36 @@ class Num < Numeric
3124
3143
  # :power 1E3 1E2 10 1 0.1 1E-2 1E-3 1E-4
3125
3144
  # :index 0 1 2 3 4 5 6 7
3126
3145
  # :rindex 7 6 5 4 3 2 1 0
3127
- def round(opt={})
3128
- opt = { :places=>opt } if opt.kind_of?(Integer)
3146
+ def round(*args)
3147
+ # Admit first integer argument
3148
+ places = args.shift if args.first.is_a?(Integer)
3149
+ opt = args.shift || {}
3150
+ raise "Invalid arguments for round" unless args.empty?
3151
+
3152
+ # Support Ruby 2.4-style parameters:
3153
+ half_rounding = opt.delete(:half)
3154
+ case half_rounding
3155
+ when :even
3156
+ opt[:rounding] = :half_even
3157
+ when :up
3158
+ opt[:rounding] = :half_up
3159
+ when :down
3160
+ opt[:rounding] = :half_down
3161
+ end
3162
+
3129
3163
  r = opt[:rounding] || :half_up
3130
3164
  as_int = false
3131
- if v=(opt[:precision] || opt[:significant_digits])
3165
+ if v = opt[:precision] || opt[:significant_digits]
3132
3166
  prec = v
3133
- elsif v=(opt[:places])
3167
+ elsif v = opt[:places] || places
3134
3168
  prec = adjusted_exponent + 1 + v
3135
- elsif v=(opt[:exponent])
3169
+ elsif v = opt[:exponent]
3136
3170
  prec = adjusted_exponent + 1 - v
3137
- elsif v=(opt[:power])
3171
+ elsif v = opt[:power]
3138
3172
  prec = adjusted_exponent + 1 - num_class.Num(v).adjusted_exponent
3139
- elsif v=(opt[:index])
3173
+ elsif v = opt[:index]
3140
3174
  prec = i+1
3141
- elsif v=(opt[:rindex])
3175
+ elsif v = opt[:rindex]
3142
3176
  prec = number_of_digits - v
3143
3177
  else
3144
3178
  prec = adjusted_exponent + 1
@@ -3203,7 +3237,7 @@ class Num < Numeric
3203
3237
 
3204
3238
  # Representation as text of a number: this is an alias of Num#format
3205
3239
  def to_s(*args)
3206
- format *args
3240
+ format(*args)
3207
3241
  end
3208
3242
 
3209
3243
  # Raises to the power of x, to modulo if given.
@@ -3886,7 +3920,6 @@ class Num < Numeric
3886
3920
  end
3887
3921
 
3888
3922
  context = define_context(num_context)
3889
- inexact = true
3890
3923
  rounding ||= context.rounding
3891
3924
  output_rounding ||= rounding
3892
3925
 
@@ -3937,7 +3970,7 @@ class Num < Numeric
3937
3970
  formatter.format(self, @coeff, @exp, rounding, p, all_digits)
3938
3971
  dec_pos,digits = formatter.adjusted_digits(output_rounding)
3939
3972
 
3940
- ds = digits.map{|d| d.to_s(output_radix)}.join
3973
+ ds = digits.map{|digit| digit.to_s(output_radix)}.join
3941
3974
  n_ds = ds.size
3942
3975
  exp = dec_pos - n_ds
3943
3976
  leftdigits = dec_pos
@@ -26,8 +26,7 @@
26
26
  # puts sin(BigDecimal('0.1'), 20)
27
27
  #
28
28
 
29
- require 'flt/float'
30
- require 'flt/bigdecimal'
29
+ require 'flt'
31
30
  require 'flt/d'
32
31
  require 'flt/b'
33
32
 
@@ -74,7 +74,9 @@ module Flt
74
74
  # Number of bits in binary representation of the positive integer n, or 0 if n == 0.
75
75
  def _nbits(x)
76
76
  raise TypeError, "The argument to _nbits should be nonnegative." if x < 0
77
- if x.is_a?(Fixnum)
77
+ # Ruby 2.1 introduced Integer#bit_length
78
+ return x.bit_length if x.respond_to? :bit_length
79
+ if x <= MAXIMUM_SMALLISH_INTEGER
78
80
  return 0 if x==0
79
81
  x.to_s(2).length
80
82
  elsif x <= NBITS_LIMIT
@@ -92,13 +94,14 @@ module Flt
92
94
  end
93
95
  NBITS_BLOCK = 32
94
96
  NBITS_LIMIT = Math.ldexp(1,Float::MANT_DIG).to_i
97
+ MAXIMUM_SMALLISH_INTEGER = (2 << 63) - 1
95
98
 
96
99
  # Number of base b digits in an integer
97
100
  def _ndigits(x, b)
98
101
  raise TypeError, "The argument to _ndigits should be nonnegative." if x < 0
99
102
  return 0 unless x.is_a?(Integer)
100
103
  return _nbits(x) if b==2
101
- if x.is_a?(Fixnum)
104
+ if x <= MAXIMUM_SMALLISH_INTEGER
102
105
  return 0 if x==0
103
106
  x.to_s(b).length
104
107
  elsif x <= NDIGITS_LIMIT
@@ -231,7 +231,7 @@ module Flt
231
231
  end
232
232
 
233
233
  # Sets (makes true) one or more flags (passes as an array)
234
- def << (flags)
234
+ def <<(flags)
235
235
  if flags.kind_of?(Array)
236
236
  set(*flags)
237
237
  else
@@ -115,7 +115,7 @@ module Flt
115
115
  end
116
116
 
117
117
  def self.[](*args)
118
- new *args
118
+ new(*args)
119
119
  end
120
120
 
121
121
  # Rationalization method that finds the fraction with
@@ -129,8 +129,8 @@ module Flt
129
129
 
130
130
  # This algorithm is derived from exercise 39 of 4.5.3 in
131
131
  # "The Art of Computer Programming", by Donald E. Knuth.
132
- def rationalize_Knuth(x)
133
- rationalization(x) do |x, dx|
132
+ def rationalize_Knuth(value)
133
+ rationalization(value) do |x, dx|
134
134
  x = to_r(x)
135
135
  dx = to_r(dx)
136
136
  xp,xq = num_den(x-dx)
@@ -47,8 +47,8 @@ module Flt
47
47
  # is the most efficient method as implemented in RPL.
48
48
  # Tony Hutchins has come up with PDR6, an improvement over PDQ2;
49
49
  # though benchmarking does not show any speed improvement under Ruby.
50
- def rationalize_Horn_Hutchins(x)
51
- rationalization(x) do |x, dx|
50
+ def rationalize_Horn_Hutchins(value)
51
+ rationalization(value) do |x, dx|
52
52
  a,b = num_den(dx)
53
53
  n,d = num_den(x)
54
54
  pc,ce = n,-d
@@ -76,8 +76,8 @@ module Flt
76
76
  # less accurate. We can achieve perfect accuracy as the other methods by doing the
77
77
  # substraction and addition with rationals, but then this method becomes less efficient than
78
78
  # the others for a low number of iterations (low precision required).
79
- def rationalize_Knuth_Goizueta(x)
80
- rationalization(x) do |x, dx|
79
+ def rationalize_Knuth_Goizueta(value)
80
+ rationalization(value) do |x, dx|
81
81
  x = to_r(x)
82
82
  dx = to_r(dx)
83
83
  xp,xq = num_den(x-dx)
@@ -110,8 +110,8 @@ module Flt
110
110
 
111
111
  # La siguiente variante realiza una iteración menos si xq<xp y una iteración más
112
112
  # si xq>xp.
113
- def rationalize_Knuth_Goizueta_b(x)
114
- rationalization(x) do |x, dx|
113
+ def rationalize_Knuth_Goizueta_b(value)
114
+ rationalization(value) do |x, dx|
115
115
  x = to_r(x)
116
116
  dx = to_r(dx)
117
117
  xq,xp = num_den(x-dx)