Class: Integer

Inherits:
Object
  • Object
show all
Defined in:
lib/long-decimal.rb

Overview

add one method to Integer

Instance Method Summary collapse

Instance Method Details

#round_to_allowed_remainders(remainders, modulus, rounding_mode = LongDecimalRoundingMode::ROUND_UNNECESSARY, zero_rounding_mode = LongDecimalRoundingMode::ZERO_ROUND_UNNECESSARY) ⇒ Object

create copy of self round in such a way that the result is congruent modulo modulus to one of the members of remainders

param1: remainders array of allowed remainders param2: modulus modulus of the remainders param3: rounding_mode rounding mode to be applied when information is

lost.   defaults  to  ROUND_UNNECESSARY,  which
means that  an exception is  thrown if rounding
would actually loose any information.

param4: zero_rounding_mode if self is zero, but zero is not among

the available remainders, it has to be
rounded to positive or negative value.
If the rounding_mode does not allow to
determine which of the two values to
use, zero_rounding_mode has to be used
to decide.

Raises:

  • (TypeError)


224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
# File 'lib/long-decimal.rb', line 224

def round_to_allowed_remainders(remainders,
                                modulus,
                                rounding_mode = LongDecimalRoundingMode::ROUND_UNNECESSARY,
                                zero_rounding_mode = LongDecimalRoundingMode::ZERO_ROUND_UNNECESSARY)

  raise TypeError, "remainders must be Array" unless remainders.kind_of? Array
  raise TypeError, "remainders must be non-empty Array" unless remainders.length > 0
  raise TypeError, "modulus #{modulus.inspect} must be integer" unless modulus.kind_of? Integer
  raise TypeError, "modulus #{modulus.inspect} must be >= 2" unless modulus >= 2
  raise TypeError, "rounding_mode #{rounding_mode.inspect} must be legal rounding rounding_mode" unless rounding_mode.kind_of? LongDecimalRoundingMode::RoundingModeClass
  raise TypeError, "ROUND_HALF_EVEN is not applicable here" if rounding_mode == LongDecimalRoundingMode::ROUND_HALF_EVEN
  raise TypeError, "zero_rounding_mode #{zero_rounding_mode.inspect} must be legal zero_rounding zero_rounding_mode" unless zero_rounding_mode.kind_of? LongDecimalRoundingMode::ZeroRoundingModeClass

  r_self     = self % modulus
  r_self_00  = r_self
  remainders = remainders.collect do |r|
    raise TypeError, "remainders must be numbers" unless r.kind_of? Integer
    r % modulus
  end
  remainders.sort!.uniq!
  r_first = remainders[0]
  r_last  = remainders[-1]
  r_first_again = r_first + modulus
  remainders.push r_first_again
  if (r_self < r_first) then
    r_self += modulus
  end
  r_lower = -1
  r_upper = -1
  remainders.each_index do |i|
    r = remainders[i]
    if (r == r_self) then
      return self
    elsif (r < r_self) then
      r_lower = r
    elsif (r > r_self) then
      r_upper  = r
      break
    end
  end
  lower = self - (r_self - r_lower)
  upper = self + (r_upper - r_self)

  unless (lower < self && self < upper)
    raise ArgumentError, "self=#{self} not in (#{lower}, #{upper})"
  end
  if (rounding_mode == LongDecimalRoundingMode::ROUND_UNNECESSARY) then
    raise ArgumentError, "mode ROUND_UNNECESSARY not applicable, self=#{self.to_s} is in open interval (#{lower}, #{upper})"
  end

  #     if (rounding_mode == LongDecimalRoundingMode::ROUND_FLOOR) then
  #       return lower
  #     elsif (rounding_mode == LongDecimalRoundingMode::ROUND_CEILING) then
  #       return upper
  #     end

  sign_self = self.sign
  if (sign_self == 0) then
    if (rounding_mode == LongDecimalRoundingMode::ROUND_UP || rounding_mode == LongDecimalRoundingMode::ROUND_DOWN \
        || lower == -upper && (rounding_mode == LongDecimalRoundingMode::ROUND_HALF_UP || rounding_mode == LongDecimalRoundingMode::ROUND_HALF_DOWN))
      if (zero_rounding_mode == LongDecimalRoundingMode::ZERO_ROUND_UNNECESSARY) then
        raise ArgumentError, "self=#{self.to_s} is 0 in open interval (#{lower}, #{upper}) and cannot be resolved with ZERO_ROUND_UNNECESSARY"
      elsif (zero_rounding_mode == LongDecimalRoundingMode::ZERO_ROUND_TO_CLOSEST_PREFER_PLUS \
             || zero_rounding_mode == LongDecimalRoundingMode::ZERO_ROUND_TO_CLOSEST_PREFER_MINUS) then
        diff = lower.abs <=> upper.abs
        if (diff < 0) then
          return lower
        elsif (diff > 0) then
          return upper
        elsif (zero_rounding_mode == LongDecimalRoundingMode::ZERO_ROUND_TO_CLOSEST_PREFER_PLUS) then
          return upper
        elsif (zero_rounding_mode == LongDecimalRoundingMode::ZERO_ROUND_TO_CLOSEST_PREFER_MINUS) then
          return lower
        else
          raise ArgumentError, "this case can never happen: zero_rounding_mode=#{zero_rounding_mode}"
        end
      elsif (zero_rounding_mode == LongDecimalRoundingMode::ZERO_ROUND_TO_PLUS) then
        return upper
      elsif (zero_rounding_mode == LongDecimalRoundingMode::ZERO_ROUND_TO_MINUS) then
        return lower
      else
        raise ArgumentError, "this case can never happen: zero_rounding_mode=#{zero_rounding_mode}"
      end
    end
  end

  # now we can assume that sign_self (and self) is != 0, which allows to decide on the rounding_mode

  if (rounding_mode == LongDecimalRoundingMode::ROUND_UP)
    # ROUND_UP goes to the closest possible value away from zero
    rounding_mode = (sign_self < 0) ? LongDecimalRoundingMode::ROUND_FLOOR : LongDecimalRoundingMode::ROUND_CEILING
  elsif (rounding_mode == LongDecimalRoundingMode::ROUND_DOWN)
    # ROUND_DOWN goes to the closest possible value towards zero or beyond zero
    rounding_mode = (sign_self < 0) ? LongDecimalRoundingMode::ROUND_CEILING : LongDecimalRoundingMode::ROUND_FLOOR
  elsif (rounding_mode == LongDecimalRoundingMode::ROUND_HALF_UP)
    # ROUND_HALF_UP goes to the closest possible value preferring away from zero
    rounding_mode = (sign_self < 0) ? LongDecimalRoundingMode::ROUND_HALF_FLOOR : LongDecimalRoundingMode::ROUND_HALF_CEILING
  elsif (rounding_mode == LongDecimalRoundingMode::ROUND_HALF_DOWN)
    # ROUND_HALF_DOWN goes to the closest possible value preferring towards zero or beyond zero
    rounding_mode = (sign_self < 0) ? LongDecimalRoundingMode::ROUND_HALF_CEILING : LongDecimalRoundingMode::ROUND_HALF_FLOOR
  end
  if (rounding_mode == LongDecimalRoundingMode::ROUND_HALF_FLOOR \
      || rounding_mode == LongDecimalRoundingMode::ROUND_HALF_CEILING) then
    d_lower = self - lower
    d_upper = upper - self
    if (d_lower < d_upper) then
      return lower
    elsif (d_upper < d_lower) then
      return upper
    elsif (rounding_mode == LongDecimalRoundingMode::ROUND_HALF_FLOOR) then
      rounding_mode = LongDecimalRoundingMode::ROUND_FLOOR
    elsif (rounding_mode == LongDecimalRoundingMode::ROUND_HALF_CEILING) then
      rounding_mode = LongDecimalRoundingMode::ROUND_CEILING
    else
      raise ArgumentError, "this case can never happen: rounding_mode=#{rounding_mode}"
    end
  end

  if (rounding_mode == LongDecimalRoundingMode::ROUND_FLOOR) then
    return lower
  elsif (rounding_mode == LongDecimalRoundingMode::ROUND_CEILING) then
    return upper
  else
    raise ArgumentError, "this case can never happen: rounding_mode=#{rounding_mode}"
  end
end

#sgnObject Also known as: signum, sign

get the sign of self -1 if self < 0

0 if self is 0 (with any number of 0s after the decimal point)

+1 if self > 0



199
200
201
# File 'lib/long-decimal.rb', line 199

def sgn
  self <=> 0
end