flt 1.5.0 → 1.5.1

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.
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)