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:


448
449
450
451
452
# File 'lib/flt/float.rb', line 448

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:


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

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:


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

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.


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

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


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

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


232
233
234
# File 'lib/flt/float.rb', line 232

def emax
  Float::MAX_EXP-1
end

#eminObject


228
229
230
# File 'lib/flt/float.rb', line 228

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)

143
144
145
# File 'lib/flt/float.rb', line 143

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

#etinyObject


236
237
238
# File 'lib/flt/float.rb', line 236

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

#etopObject


240
241
242
# File 'lib/flt/float.rb', line 240

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

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

Yields:

  • (_self)

Yield Parameters:


488
489
490
# File 'lib/flt/float.rb', line 488

def eval
  yield self
end

#exact?Boolean

Returns:

  • (Boolean)

219
220
221
# File 'lib/flt/float.rb', line 219

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

181
182
183
184
185
# File 'lib/flt/float.rb', line 181

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


126
127
128
# File 'lib/flt/float.rb', line 126

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


134
135
136
# File 'lib/flt/float.rb', line 134

def int_radix_power(n)
  1 << n
end

#ln(x) ⇒ Object


480
481
482
# File 'lib/flt/float.rb', line 480

def ln(x)
  log(x)
end

#math(*parameters, &blk) ⇒ Object


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

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


211
212
213
# File 'lib/flt/float.rb', line 211

def maximum_coefficient
  int_radix_power(precision)-1
end

#maximum_finite(sign = +1) ⇒ Object

maximum finite Float value, with specified sign


203
204
205
# File 'lib/flt/float.rb', line 203

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)


193
194
195
# File 'lib/flt/float.rb', line 193

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


198
199
200
# File 'lib/flt/float.rb', line 198

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)


188
189
190
# File 'lib/flt/float.rb', line 188

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

#minimum_normalized_coefficientObject


215
216
217
# File 'lib/flt/float.rb', line 215

def minimum_normalized_coefficient
  num_class.int_radix_power(precision-1)
end

#minus(x) ⇒ Object


383
384
385
# File 'lib/flt/float.rb', line 383

def minus(x)
  -x.to_f
end

#nanObject

NaN (not a number value)


116
117
118
# File 'lib/flt/float.rb', line 116

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

#necessary_digits(b) ⇒ Object


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

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


248
249
250
# File 'lib/flt/float.rb', line 248

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

#next_plus(x) ⇒ Object


244
245
246
# File 'lib/flt/float.rb', line 244

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

#next_toward(x, y) ⇒ Object


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

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)

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

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

#Num(*args) ⇒ Object


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

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


95
96
97
# File 'lib/flt/float.rb', line 95

def num_class
  Float
end

#one_halfObject


130
131
132
# File 'lib/flt/float.rb', line 130

def one_half
  0.5
end

#piObject


484
485
486
# File 'lib/flt/float.rb', line 484

def pi
  Math::PI
end

#plus(x) ⇒ Object


379
380
381
# File 'lib/flt/float.rb', line 379

def plus(x)
  x.to_f
end

#precisionObject


207
208
209
# File 'lib/flt/float.rb', line 207

def precision
  Float::MANT_DIG
end

#radixObject


111
112
113
# File 'lib/flt/float.rb', line 111

def radix
  Float::RADIX
end

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


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

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


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

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


224
225
226
# File 'lib/flt/float.rb', line 224

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)


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

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)

359
360
361
# File 'lib/flt/float.rb', line 359

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


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

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

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

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)

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

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.


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

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


387
388
389
# File 'lib/flt/float.rb', line 387

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


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

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


121
122
123
# File 'lib/flt/float.rb', line 121

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