Class: Flt::FloatContext

Inherits:
Object show all
Includes:
Singleton
Defined in:
lib/flt/float.rb

Overview

Context class with some of the Flt::Num context functionality, to allow the use of Float numbers similarly to other Num values; this eases the implementation of functions compatible with either Num or Float values.

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.float_binary_operator(method, op) ⇒ Object

:nodoc:



446
447
448
449
450
# File 'lib/flt/float.rb', line 446

def float_binary_operator(method, op) #:nodoc:
  define_method(method) do |x,y|
    x.to_f.send(op,y)
  end
end

.float_method(*methods) ⇒ Object

:nodoc:



433
434
435
436
437
438
439
440
441
442
443
444
# File 'lib/flt/float.rb', line 433

def float_method(*methods) #:nodoc:
  methods.each do |method|
    if method.is_a?(Array)
      float_method, context_method = method
    else
      float_method = context_method = method
    end
    define_method(context_method) do |x|
      x.to_f.send float_method
    end
  end
end

.math_function(*methods) ⇒ Object

:nodoc:



452
453
454
455
456
457
458
459
460
461
462
463
# File 'lib/flt/float.rb', line 452

def math_function(*methods) #:nodoc:
  methods.each do |method|
    define_method(method) do |*args|
      x = args.shift.to_f
      Math.send(method, x, *args)
    end
    # TODO: consider injecting the math methods into Float
    # Float.send(:define_method, method) do |*args|
    #   Math.send(method, self, *args)
    # end
  end
end

.neighbours(x) ⇒ Object

Compute the adjacent floating point values: largest value not larger than this and smallest not smaller.



407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
# File 'lib/flt/float.rb', line 407

def neighbours(x)
  f,e = Math.frexp(x.to_f)
  e = Float::MIN_EXP if f==0
  e = [Float::MIN_EXP,e].max
  dx = Math.ldexp(1,e-Float::MANT_DIG) #Math.ldexp(Math.ldexp(1.0,-Float::MANT_DIG),e)

  min_f = 0.5 #0.5==Math.ldexp(2**(bits-1),-Float::MANT_DIG)
  max_f = 1.0 - Math.ldexp(1,-Float::MANT_DIG)

  if f==max_f
    high = x + dx*2
  elsif f==-min_f && e!=Float::MIN_EXP
    high = x + dx/2
  else
    high = x + dx
  end
  if e==Float::MIN_EXP || f!=min_f
    low = x - dx
  elsif f==-max_f
    high = x - dx*2
  else
    low = x - dx/2
  end
  [low, high]
end

Instance Method Details

#copy_sign(x, y) ⇒ Object

Return copy of x with the sign of y



276
277
278
279
280
281
282
283
284
285
286
287
288
# File 'lib/flt/float.rb', line 276

def copy_sign(x, y)
  self_sign = sign(x)
  other_sign = y.is_a?(Integer) ? (y < 0 ? -1 : +1) : sign(y)
  if self_sign && other_sign
    if self_sign == other_sign
      x.to_f
    else
      -x.to_f
    end
  else
    nan
  end
end

#emaxObject



230
231
232
# File 'lib/flt/float.rb', line 230

def emax
  Float::MAX_EXP-1
end

#eminObject



226
227
228
# File 'lib/flt/float.rb', line 226

def emin
  Float::MIN_EXP-1
end

#epsilon(sign = +1) ⇒ Object

This is the difference between 1.0 and the smallest floating-point value greater than 1.0, radix_power(1-significand_precision)

We have:

Float.epsilon == (1.0.next-1.0)


141
142
143
# File 'lib/flt/float.rb', line 141

def epsilon(sign=+1)
  (sign < 0) ? -Float::EPSILON : Float::EPSILON
end

#etinyObject



234
235
236
# File 'lib/flt/float.rb', line 234

def etiny
  Float::MIN_EXP - Float::MANT_DIG
end

#etopObject



238
239
240
# File 'lib/flt/float.rb', line 238

def etop
  Float::MAX_EXP - Float::MANT_DIG
end

#eval {|_self| ... } ⇒ Object

Yields:

  • (_self)

Yield Parameters:



486
487
488
# File 'lib/flt/float.rb', line 486

def eval
  yield self
end

#exact?Boolean

Returns:

  • (Boolean)


217
218
219
# File 'lib/flt/float.rb', line 217

def exact?
  false
end

#half_epsilon(sign = +1) ⇒ Object

This is the maximum relative error corresponding to 1/2 ulp:

(radix/2)*radix_power(-significand_precision) == epsilon/2

This is called “machine epsilon” in [Goldberg] We have:

Float.half_epsilon == 0.5*Float.epsilon


179
180
181
182
183
# File 'lib/flt/float.rb', line 179

def half_epsilon(sign=+1)
  # 0.5*epsilon(sign)
  f,e = Math.frexp(1)
  Math.ldexp(f, e-Float::MANT_DIG)
end

#infinity(sign = +1) ⇒ Object

infinity value with specified sign



124
125
126
# File 'lib/flt/float.rb', line 124

def infinity(sign=+1)
  (sign < 0) ? -1.0/0.0 : 1.0/0.0 # Ruby 1.9.2: (sing < 0) ? -Float::INFINITY : Float::INFINITY
end

#int_radix_power(n) ⇒ Object



132
133
134
# File 'lib/flt/float.rb', line 132

def int_radix_power(n)
  1 << n
end

#ln(x) ⇒ Object



478
479
480
# File 'lib/flt/float.rb', line 478

def ln(x)
  log(x)
end

#math(*parameters, &blk) ⇒ Object



490
491
492
493
494
495
496
497
# File 'lib/flt/float.rb', line 490

def math(*parameters, &blk)
  if parameters.empty?
    self.instance_eval(&blk)
  else
    # needs instance_exe (available in Ruby 1.9, ActiveRecord; TODO: include implementation here)
    self.instance_exec(*parameters, &blk)
  end
end

#maximum_coefficientObject



209
210
211
# File 'lib/flt/float.rb', line 209

def maximum_coefficient
  int_radix_power(precision)-1
end

#maximum_finite(sign = +1) ⇒ Object

maximum finite Float value, with specified sign



201
202
203
# File 'lib/flt/float.rb', line 201

def maximum_finite(sign=+1)
  (sign < 0) ? -Float::MAX : Float::MAX
end

#maximum_subnormal(sign = +1) ⇒ Object

maximum subnormal (denormalized) Float value (with specified sign)



191
192
193
# File 'lib/flt/float.rb', line 191

def maximum_subnormal(sign=+1)
  (sign < 0) ? -Float::MAX_D : Float::MAX_D
end

#minimum_nonzero(sign = +1) ⇒ Object

minimum (subnormal) nonzero Float value, with specified sign



196
197
198
# File 'lib/flt/float.rb', line 196

def minimum_nonzero(sign=+1)
  (sign < 0) ? -Float::MIN_D : Float::MIN_D
end

#minimum_normal(sign = +1) ⇒ Object

minimum normal Float value (with specified sign)



186
187
188
# File 'lib/flt/float.rb', line 186

def minimum_normal(sign=+1)
  (sign < 0) ? -Float::MIN_N : Float::MIN_N
end

#minimum_normalized_coefficientObject



213
214
215
# File 'lib/flt/float.rb', line 213

def minimum_normalized_coefficient
  num_class.int_radix_power(precision-1)
end

#minus(x) ⇒ Object



381
382
383
# File 'lib/flt/float.rb', line 381

def minus(x)
  -x.to_f
end

#nanObject

NaN (not a number value)



114
115
116
# File 'lib/flt/float.rb', line 114

def nan
  0.0/0.0 # Ruby 1.9.2: Float::NAN
end

#necessary_digits(b) ⇒ Object



509
510
511
512
513
514
515
516
517
# File 'lib/flt/float.rb', line 509

def necessary_digits(b)
  if b == 10
    Float::DECIMAL_DIG
  elsif b == radix
    precision
  else
   (precision*log(radix, b)).ceil + 1
  end
end

#next_minus(x) ⇒ Object



246
247
248
# File 'lib/flt/float.rb', line 246

def next_minus(x)
  Flt::FloatContext.neighbours(x).first
end

#next_plus(x) ⇒ Object



242
243
244
# File 'lib/flt/float.rb', line 242

def next_plus(x)
  Flt::FloatContext.neighbours(x).last
end

#next_toward(x, y) ⇒ Object



250
251
252
253
254
255
256
257
258
259
# File 'lib/flt/float.rb', line 250

def next_toward(x, y)
  x, y = x.to_f, y.to_f
  comparison = x <=> y
  return x.copy_sign(y) if comparison == 0
  if comparison == -1
    result = x.next_plus(context)
  else # comparison == 1
    result = x.next_minus(context)
  end
end

#normal?(x) ⇒ Boolean

Returns:

  • (Boolean)


361
362
363
364
365
366
367
# File 'lib/flt/float.rb', line 361

def normal?(x)
  if x.special? || x.zero?
    false
  else
    x.abs >= Float::MIN_N
  end
end

#Num(*args) ⇒ Object



97
98
99
100
101
102
103
104
105
106
107
# File 'lib/flt/float.rb', line 97

def Num(*args)
  args.flatten!
  case args.size
  when 1
    Float(*args)
  when 2
    Math.ldexp(args[0],args[1])
  when 3
    Math.ldexp(args[0]*args[1],args[2])
  end
end

#num_classObject



93
94
95
# File 'lib/flt/float.rb', line 93

def num_class
  Float
end

#one_halfObject



128
129
130
# File 'lib/flt/float.rb', line 128

def one_half
  0.5
end

#piObject



482
483
484
# File 'lib/flt/float.rb', line 482

def pi
  Math::PI
end

#plus(x) ⇒ Object



377
378
379
# File 'lib/flt/float.rb', line 377

def plus(x)
  x.to_f
end

#precisionObject



205
206
207
# File 'lib/flt/float.rb', line 205

def precision
  Float::MANT_DIG
end

#radixObject



109
110
111
# File 'lib/flt/float.rb', line 109

def radix
  Float::RADIX
end

#rationalize(x, tol = Flt.Tolerance(:epsilon), strict = false) ⇒ Object



389
390
391
392
393
394
395
396
397
398
399
400
401
402
# File 'lib/flt/float.rb', line 389

def rationalize(x, tol = Flt.Tolerance(:epsilon), strict=false)
  if !strict && x.respond_to?(:rationalize) && !(Integer === tol)
    # Float#rationalize was introduced in Ruby 1.9.1
    tol = Tolerance(tol)
    x.rationalize(tol[x])
  else
    case tol
    when Integer
      Rational(*Support::Rationalizer.max_denominator(x, tol, Float))
    else
      Rational(*Support::Rationalizer[tol].rationalize(x))
    end
  end
end

#representable_digits(b) ⇒ Object



499
500
501
502
503
504
505
506
507
# File 'lib/flt/float.rb', line 499

def representable_digits(b)
  if b == 10
    Float::DIG
  elsif b == radix
    precision
  else
   ((precision-1)*log(radix, b)).floor
  end
end

#roundingObject

detect actual rounding mode



222
223
224
# File 'lib/flt/float.rb', line 222

def rounding
  Flt::Support::AuxiliarFunctions.detect_float_rounding
end

#sign(x) ⇒ Object

Sign: -1 for minus, +1 for plus, nil for nan (note that Float zero is signed)



262
263
264
265
266
267
268
269
270
271
272
273
# File 'lib/flt/float.rb', line 262

def sign(x)
  x = x.to_f
  if x.nan?
    nil
  elsif x.zero?
    # Note that (x.to_s[0,1] == "-" ? -1 : +1) fails under mswin32
    # because in that platform (-0.0).to_s == '0.0'
    (1/x < 0) ? -1 : +1
  else
    x < 0 ? -1 : +1
  end
end

#special?(x) ⇒ Boolean

Returns:

  • (Boolean)


357
358
359
# File 'lib/flt/float.rb', line 357

def special?(x)
  x.nan? || x.infinite?
end

#split(x) ⇒ Object

Returns the internal representation of the number, composed of:

  • a sign which is +1 for plus and -1 for minus

  • a coefficient (significand) which is a nonnegative integer

  • an exponent (an integer) or :inf, :nan or :snan for special values

The value of non-special numbers is sign*coefficient*10^exponent



295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
# File 'lib/flt/float.rb', line 295

def split(x)
  x = x.to_f
  sign = sign(x)
  if x.nan?
    exp = :nan
  elsif x.infinite?
    exp = :inf
  else
    coeff,exp = Math.frexp(x)
    coeff = coeff.abs
    if exp < Float::MIN_EXP
      # denormalized number
      coeff = Math.ldexp(coeff, exp-Float::MIN_EXP+Float::MANT_DIG).to_i
      exp = Float::MIN_EXP-Float::MANT_DIG
    else
      # normalized number
      coeff = Math.ldexp(coeff, Float::MANT_DIG).to_i
      exp -= Float::MANT_DIG
    end
  end
  [sign, coeff, exp]
end

#strict_epsilon(sign = +1, round = nil) ⇒ Object

The strict epsilon is the smallest value that produces something different from 1.0 wehen added to 1.0. It may be smaller than the general epsilon, because of the particular rounding rules used with the floating point format. This is only meaningful when well-defined rules are used for rounding the result of floating-point addition.

We have:

(Float.strict_epsilon+1.0) == 1.0.next
(Float.strict_epsilon.prev+1.0) == 1.0


154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/flt/float.rb', line 154

def strict_epsilon(sign=+1, round=nil)
  # We don't rely on Float::ROUNDS
  eps = minimum_nonzero
  unless (1.0+eps) > 1.0
    f,e = Math.frexp(1)
    eps = Math.ldexp(f.next,e-Float::MANT_DIG)
    if (1.0+eps) > 1.0
      eps
    else
      eps = Math.ldexp(f,e-Float::MANT_DIG)
      unless (1.0+eps) > 1.0
      else
        eps = Math.ldexp(f,e-Float::MANT_DIG+1)
      end
    end
  end
  eps
end

#subnormal?Boolean

Returns:

  • (Boolean)


369
370
371
372
373
374
375
# File 'lib/flt/float.rb', line 369

def subnormal?
  if x.special? || x.zero?
    false
  else
    x.abs < Float::MIN_N
  end
end

#to_int_scale(x) ⇒ Object

Return the value of the number as an signed integer and a scale.



319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
# File 'lib/flt/float.rb', line 319

def to_int_scale(x)
  x = x.to_f
  if special?(x)
    nil
  else
    coeff,exp = Math.frexp(x)
    coeff = coeff
    if exp < Float::MIN_EXP
      # denormalized number
      coeff = Math.ldexp(coeff, exp-Float::MIN_EXP+Float::MANT_DIG).to_i
      exp = Float::MIN_EXP-Float::MANT_DIG
    else
      # normalized number
      coeff = Math.ldexp(coeff, Float::MANT_DIG).to_i
      exp -= Float::MANT_DIG
    end
    [coeff, exp]
  end
end

#to_r(x) ⇒ Object



385
386
387
# File 'lib/flt/float.rb', line 385

def to_r(x)
  Support::Rationalizer.to_r(x)
end

#ulp(x, mode = :low) ⇒ Object

ulp (unit in the last place) according to the definition proposed by J.M. Muller in “On the definition of ulp(x)” INRIA No. 5504



341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
# File 'lib/flt/float.rb', line 341

def ulp(x, mode=:low)
  x = x.to_f
  return x if x.nan?
  x = x.abs
  if x < Math.ldexp(1,Float::MIN_EXP) # x < Float::RADIX*Float::MIN_N
    x = Math.ldexp(1,Float::MIN_EXP-Float::MANT_DIG) # res = Float::MIN_D
  elsif x > Float::MAX # x > Math.ldexp(1-Math.ldexp(1,-Float::MANT_DIG),Float::MAX_EXP)
    x = Math.ldexp(1,Float::MAX_EXP-Float::MANT_DIG) # res = Float::MAX - Float::MAX.prev
  else
    f,e = Math.frexp(x.to_f)
    e -= 1 if f==Math.ldexp(1,-1) if mode==:low # assign the smaller ulp to radix powers
    x = Math.ldexp(1,e-Float::MANT_DIG)
  end
  x
end

#zero(sign = +1) ⇒ Object

zero value with specified sign



119
120
121
# File 'lib/flt/float.rb', line 119

def zero(sign=+1)
  (sign < 0) ? -0.0 : 0.0
end