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:



425
426
427
428
429
# File 'lib/flt/float.rb', line 425

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:



412
413
414
415
416
417
418
419
420
421
422
423
# File 'lib/flt/float.rb', line 412

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:



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

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.



386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
# File 'lib/flt/float.rb', line 386

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



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

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



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

def emax
  Float::MAX_EXP-1
end

#eminObject



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

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)


139
140
141
# File 'lib/flt/float.rb', line 139

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

#etinyObject



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

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

#etopObject



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

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

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

Yields:

  • (_self)

Yield Parameters:



465
466
467
# File 'lib/flt/float.rb', line 465

def eval
  yield self
end

#exact?Boolean

Returns:

  • (Boolean)


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

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


177
178
179
180
181
# File 'lib/flt/float.rb', line 177

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



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

def int_radix_power(n)
  1 << n
end

#ln(x) ⇒ Object



457
458
459
# File 'lib/flt/float.rb', line 457

def ln(x)
  log(x)
end

#math(*parameters, &blk) ⇒ Object



469
470
471
472
473
474
475
476
# File 'lib/flt/float.rb', line 469

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



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

def maximum_coefficient
  int_radix_power(precision)-1
end

#maximum_finite(sign = +1) ⇒ Object

maximum finite Float value, with specified sign



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

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)



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

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



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

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)



184
185
186
# File 'lib/flt/float.rb', line 184

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

#minimum_normalized_coefficientObject



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

def minimum_normalized_coefficient
  num_class.int_radix_power(precision-1)
end

#minus(x) ⇒ Object



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

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

#next_minus(x) ⇒ Object



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

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

#next_plus(x) ⇒ Object



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

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

#next_toward(x, y) ⇒ Object



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

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)


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

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

#piObject



461
462
463
# File 'lib/flt/float.rb', line 461

def pi
  Math::PI
end

#plus(x) ⇒ Object



375
376
377
# File 'lib/flt/float.rb', line 375

def plus(x)
  x.to_f
end

#precisionObject



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

def precision
  Float::MANT_DIG
end

#radixObject



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

def radix
  Float::RADIX
end

#roundingObject

detect actual rounding mode



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

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)



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

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)


355
356
357
# File 'lib/flt/float.rb', line 355

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



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

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


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

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)


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

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.



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

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

#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



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

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