diff --git a/core/src/main/java/org/jruby/ext/bigdecimal/RubyBigDecimal.java b/core/src/main/java/org/jruby/ext/bigdecimal/RubyBigDecimal.java index 1f4068c10ee..2dfc76af742 100644 --- a/core/src/main/java/org/jruby/ext/bigdecimal/RubyBigDecimal.java +++ b/core/src/main/java/org/jruby/ext/bigdecimal/RubyBigDecimal.java @@ -701,12 +701,8 @@ public IRubyObject initialize_copy(IRubyObject original) { return this; } - public IRubyObject op_mod(ThreadContext context, IRubyObject arg) { - return op_mod19(context, arg); - } - @JRubyMethod(name = {"%", "modulo"}, required = 1) - public IRubyObject op_mod19(ThreadContext context, IRubyObject other) { + public IRubyObject op_mod(ThreadContext context, IRubyObject other) { // TODO: full-precision divmod is 1000x slower than MRI! RubyBigDecimal val = getVpValue19(context, other, false); @@ -724,14 +720,20 @@ public IRubyObject op_mod19(ThreadContext context, IRubyObject other) { return new RubyBigDecimal(context.runtime, modulo).setResult(); } + @Deprecated + public IRubyObject op_mod19(ThreadContext context, IRubyObject arg) { + return op_mod(context, arg); + } + @Override + @JRubyMethod(name = "remainder", required = 1) public IRubyObject remainder(ThreadContext context, IRubyObject arg) { - return remainder19(context, arg); + return remainderInternal(context, getVpValue19(context, arg, false), arg); } - @JRubyMethod(name = "remainder", required = 1) + @Deprecated public IRubyObject remainder19(ThreadContext context, IRubyObject arg) { - return remainderInternal(context, getVpValue19(context, arg, false), arg); + return remainder(context, arg); } private IRubyObject remainderInternal(ThreadContext context, RubyBigDecimal val, IRubyObject arg) { @@ -744,21 +746,18 @@ private IRubyObject remainderInternal(ThreadContext context, RubyBigDecimal val, return new RubyBigDecimal(context.runtime, value.remainder(val.value)).setResult(); } + @JRubyMethod(name = "*", required = 1) public IRubyObject op_mul(ThreadContext context, IRubyObject arg) { - return op_mul19(context, arg); + return mult2(context, arg, vpPrecLimit(context.runtime)); } - @JRubyMethod(name = "*", required = 1) + @Deprecated public IRubyObject op_mul19(ThreadContext context, IRubyObject arg) { - return mult219(context, arg, vpPrecLimit(context.runtime)); - } - - public IRubyObject mult2(ThreadContext context, IRubyObject b, IRubyObject n) { - return mult219(context, b, n); + return op_mul(context, arg); } @JRubyMethod(name = "mult", required = 2) - public IRubyObject mult219(ThreadContext context, IRubyObject b, IRubyObject n) { + public IRubyObject mult2(ThreadContext context, IRubyObject b, IRubyObject n) { RubyBigDecimal val = getVpValue19(context, b, false); if (val == null) { // TODO: what about n arg? return callCoerced(context, sites(context).op_times, b, true); @@ -791,6 +790,11 @@ private RubyBigDecimal multInternal(final Ruby runtime, RubyBigDecimal val, IRub return new RubyBigDecimal(runtime, res).setResult(); } + @Deprecated + public IRubyObject mult219(ThreadContext context, IRubyObject b, IRubyObject n) { + return mult2(context, b, n); + } + // Calculate appropriate zero or infinity depending on exponent... private RubyBigDecimal newPowOfInfinity(ThreadContext context, IRubyObject exp) { if (Numeric.f_negative_p(context, exp)) { @@ -815,20 +819,11 @@ private static IRubyObject vpPrecLimit(final Ruby runtime) { // @Deprecated public IRubyObject op_pow(IRubyObject arg) { - return op_pow19(getRuntime().getCurrentContext(), arg); - } - - public RubyBigDecimal op_pow(final ThreadContext context, IRubyObject arg) { - return op_pow19(context, arg); - } - - // @Deprecated - public IRubyObject op_pow19(IRubyObject exp) { - return op_pow19(getRuntime().getCurrentContext(), exp); + return op_pow(getRuntime().getCurrentContext(), arg); } @JRubyMethod(name = {"**", "power"}, required = 1) - public RubyBigDecimal op_pow19(ThreadContext context, IRubyObject exp) { + public RubyBigDecimal op_pow(final ThreadContext context, IRubyObject exp) { final Ruby runtime = context.runtime; if ( ! (exp instanceof RubyNumeric) ) { @@ -869,6 +864,16 @@ public RubyBigDecimal op_pow19(ThreadContext context, IRubyObject exp) { return new RubyBigDecimal(runtime, pow); } + @Deprecated + public IRubyObject op_pow19(IRubyObject exp) { + return op_pow(getRuntime().getCurrentContext(), exp); + } + + @Deprecated + public RubyBigDecimal op_pow19(ThreadContext context, IRubyObject exp) { + return op_pow(context, exp); + } + private BigDecimal powNegative(final int times) { // Note: MRI has a very non-trivial way of calculating the precision, // so we use very simple approximation here: @@ -876,22 +881,24 @@ private BigDecimal powNegative(final int times) { return value.pow(times, new MathContext(precision, RoundingMode.HALF_UP)); } + @JRubyMethod(name = "+") public IRubyObject op_plus(ThreadContext context, IRubyObject b) { - return op_plus19(context, b); + return addInternal(context, getVpValue19(context, b, false), b, vpPrecLimit(context.runtime)); } - @JRubyMethod(name = "+") + @Deprecated public IRubyObject op_plus19(ThreadContext context, IRubyObject b) { - return addInternal(context, getVpValue19(context, b, false), b, vpPrecLimit(context.runtime)); + return op_plus(context, b); } + @JRubyMethod(name = "add") public IRubyObject add2(ThreadContext context, IRubyObject b, IRubyObject digits) { - return add219(context, b, digits); + return addInternal(context, getVpValue19(context, b, false), b, digits); } - @JRubyMethod(name = "add") + @Deprecated public IRubyObject add219(ThreadContext context, IRubyObject b, IRubyObject digits) { - return addInternal(context, getVpValue19(context, b, false), b, digits); + return add2(context, b, digits); } private IRubyObject addInternal(ThreadContext context, RubyBigDecimal val, IRubyObject b, IRubyObject digits) { @@ -956,25 +963,37 @@ public IRubyObject op_uplus() { return this; } - public IRubyObject op_minus(ThreadContext context, IRubyObject b) { - return op_minus19(context, b); + @Override + @JRubyMethod(name = "-@") + public IRubyObject op_uminus(ThreadContext context) { + if (isNaN()) return newNaN(context.runtime); + if (isInfinity()) return newInfinity(context.runtime, -infinitySign); + if (isZero()) return newZero(context.runtime, -zeroSign); + + return new RubyBigDecimal(context.runtime, value.negate()); } @JRubyMethod(name = "-", required = 1) - public IRubyObject op_minus19(ThreadContext context, IRubyObject b) { + public IRubyObject op_minus(ThreadContext context, IRubyObject b) { return subInternal(context, getVpValue19(context, b, true), b); } - public IRubyObject sub2(ThreadContext context, IRubyObject b, IRubyObject n) { - return sub219(context, b, n); + @Deprecated + public IRubyObject op_minus19(ThreadContext context, IRubyObject b) { + return op_minus(context, b); } @JRubyMethod(name = "sub", required = 2) - public IRubyObject sub219(ThreadContext context, IRubyObject b, IRubyObject n) { + public IRubyObject sub2(ThreadContext context, IRubyObject b, IRubyObject n) { // FIXME: Missing handling of n return subInternal(context, getVpValue19(context, b, false), b); } + @Deprecated + public IRubyObject sub219(ThreadContext context, IRubyObject b, IRubyObject n) { + return sub2(context, b, n); + } + private IRubyObject subInternal(ThreadContext context, RubyBigDecimal val, IRubyObject b) { if (val == null) return callCoerced(context, sites(context).op_minus, b); @@ -1010,33 +1029,13 @@ private RubyBigDecimal handleMinusSpecialValues(ThreadContext context, RubyBigDe return null; } - @JRubyMethod(name = "-@") - @Override - public IRubyObject op_uminus(ThreadContext context) { - if (isNaN()) return newNaN(context.runtime); - if (isInfinity()) return newInfinity(context.runtime, -infinitySign); - if (isZero()) return newZero(context.runtime, -zeroSign); - - return new RubyBigDecimal(getRuntime(), value.negate()); - } - - public IRubyObject op_quo(ThreadContext context, IRubyObject other) { - return op_quo20(context, other); - } - - public IRubyObject op_quo19(ThreadContext context, IRubyObject other) { - return op_quo19_20(context, other); - } - @JRubyMethod(name = {"/", "quo"}) - public IRubyObject op_quo20(ThreadContext context, IRubyObject other) { - return op_quo19_20(context, other); - } - - private IRubyObject op_quo19_20(ThreadContext context, IRubyObject other) { + public IRubyObject op_quo(ThreadContext context, IRubyObject other) { RubyBigDecimal val = getVpValue19(context, other, false); if (val == null) return callCoerced(context, sites(context).op_quo, other, true); + if (isNaN() || val.isNaN()) return newNaN(context.runtime); + // regular division with some default precision // proper algorithm to set the precision // the precision is multiple of 4 @@ -1045,70 +1044,77 @@ private IRubyObject op_quo19_20(ThreadContext context, IRubyObject other) { int pow = len / 4; int precision = (pow + 1) * 4 * 2; - return op_div(context, val, context.runtime.newFixnum(precision)); + return divWithScale(context, val, precision); } - public IRubyObject op_div(ThreadContext context, IRubyObject other) { - // integer division - RubyBigDecimal val = getVpValue(context, other, false); - if (val == null) return callCoerced(context, sites(context).div, other); - if (isNaN() || val.isZero() || val.isNaN()) return newNaN(context.runtime); - if (isInfinity() || val.isInfinity()) return newNaN(context.runtime); + @Deprecated + public IRubyObject op_quo19(ThreadContext context, IRubyObject other) { + return op_quo(context, other); + } - return new RubyBigDecimal(context.runtime, - this.value.divideToIntegralValue(val.value)).setResult(); + @Deprecated + public IRubyObject op_quo20(ThreadContext context, IRubyObject other) { + return op_quo(context, other); } @JRubyMethod(name = "div") - public IRubyObject op_div19(ThreadContext context, IRubyObject r) { + public IRubyObject op_div(ThreadContext context, IRubyObject r) { RubyBigDecimal val = getVpValue19(context, r, false); if (val == null) return callCoerced(context, sites(context).div, r, true); if (isNaN() || val.isNaN()) throw context.runtime.newFloatDomainError("Computation results to 'NaN'"); - if (isInfinity() && val.isOne()) throw context.runtime.newFloatDomainError("Computation results to 'Infinity'"); + if (isInfinity()) { // NOTE: MRI is inconsistent with div(r, d) impl + if (val.isInfinity()) { + throw context.runtime.newFloatDomainError("Computation results to 'NaN'(Not a Number)"); + } + throw context.runtime.newFloatDomainError("Computation results to 'Infinity'"); + } + if (val.isInfinity()) return newZero(context.runtime, value.signum() * val.infinitySign); - if (val.isInfinity()) return newZero(context.runtime, val.infinitySign); + if (val.isZero()) throw context.runtime.newZeroDivisionError(); - if (isZero() || val.isZero()) throw context.runtime.newZeroDivisionError(); + return new RubyBigDecimal(context.runtime, this.value.divideToIntegralValue(val.value)).setResult(); + } + @Deprecated + public final IRubyObject op_div19(ThreadContext context, IRubyObject r) { return op_div(context, r); } + @JRubyMethod(name = "div") public IRubyObject op_div(ThreadContext context, IRubyObject other, IRubyObject digits) { - // TODO: take BigDecimal.mode into account. - - int scale = RubyNumeric.fix2int(digits); - RubyBigDecimal val = getVpValue(context, other, false); if (val == null) return callCoerced(context, sites(context).div, other, true); - if (isNaN() || (isZero() && val.isZero()) || val.isNaN()) return newNaN(context.runtime); + + if (isNaN() || val.isNaN()) { + throw context.runtime.newFloatDomainError("Computation results to 'NaN'"); + } + return divWithScale(context, val, RubyNumeric.fix2int(digits)); + } + + private IRubyObject divWithScale(ThreadContext context, RubyBigDecimal val, int scale) { + if (isInfinity()) { + if (val.isInfinity()) return newNaN(context.runtime); + return newInfinity(context.runtime, infinitySign * val.value.signum()); + } + if (val.isInfinity()) return newZero(context.runtime, value.signum() * val.infinitySign); if (val.isZero()) { + if (isZero()) return newNaN(context.runtime); int sign1 = isInfinity() ? infinitySign : value.signum(); return newInfinity(context.runtime, sign1 * val.zeroSign); } - - if (isInfinity() && !val.isInfinity()) return newInfinity(context.runtime, infinitySign * val.value.signum()); - if (!isInfinity() && val.isInfinity()) return newZero(context.runtime, value.signum() * val.infinitySign); - if (isInfinity() && val.isInfinity()) return newNaN(context.runtime); if (isZero()) return newZero(context.runtime, zeroSign * val.value.signum()); // MRI behavior: "If digits is 0, the result is the same as the / operator." - if (scale == 0) return op_quo(context, other); + if (scale == 0) return op_quo(context, val); MathContext mathContext = new MathContext(scale, getRoundingMode(context.runtime)); return new RubyBigDecimal(context.runtime, value.divide(val.value, mathContext)).setResult(scale); } - @JRubyMethod(name = "div") - public IRubyObject op_div19(ThreadContext context, IRubyObject other, IRubyObject digits) { - RubyBigDecimal val = getVpValue(context, other, false); - if (val == null) return callCoerced(context, sites(context).div, other, true); - - if (isNaN() || val.isNaN()) { - throw context.runtime.newFloatDomainError("Computation results to 'NaN'"); - } - + @Deprecated + public final IRubyObject op_div19(ThreadContext context, IRubyObject other, IRubyObject digits) { return op_div(context, other, digits); } @@ -1126,17 +1132,16 @@ private IRubyObject cmp(ThreadContext context, final IRubyObject arg, final char } else { if (isNaN() || rb.isNaN()) return (op == '*') ? context.nil : context.runtime.getFalse(); - e = infinitySign != 0 || rb.infinitySign != 0 ? - infinitySign - rb.infinitySign : value.compareTo(rb.value); + e = infinitySign != 0 || rb.infinitySign != 0 ? infinitySign - rb.infinitySign : value.compareTo(rb.value); } switch(op) { - case '*': return context.runtime.newFixnum(e); - case '=': return context.runtime.newBoolean(e == 0); - case '!': return context.runtime.newBoolean(e != 0); - case 'G': return context.runtime.newBoolean(e >= 0); - case '>': return context.runtime.newBoolean(e > 0); - case 'L': return context.runtime.newBoolean(e <= 0); - case '<': return context.runtime.newBoolean(e < 0); + case '*': return context.runtime.newFixnum(e); + case '=': return context.runtime.newBoolean(e == 0); + case '!': return context.runtime.newBoolean(e != 0); + case 'G': return context.runtime.newBoolean(e >= 0); + case '>': return context.runtime.newBoolean(e > 0); + case 'L': return context.runtime.newBoolean(e <= 0); + case '<': return context.runtime.newBoolean(e < 0); } return context.nil; } @@ -1189,7 +1194,7 @@ public IRubyObject ceil(ThreadContext context, IRubyObject arg) { if (value.scale() <= n) return this; // no rounding necessary - return new RubyBigDecimal(getRuntime(), value.setScale(n, RoundingMode.CEILING)); + return new RubyBigDecimal(context.runtime, value.setScale(n, RoundingMode.CEILING)); } @JRubyMethod @@ -1250,14 +1255,9 @@ public RubyNumeric multiplyWith(ThreadContext context, RubyBignum value) { return (RubyNumeric)op_mul(context, value); } - @Override - public IRubyObject divmod(ThreadContext context, IRubyObject other) { - return divmod19(context, other); - } - @Override @JRubyMethod(name = "divmod") - public IRubyObject divmod19(ThreadContext context, IRubyObject other) { + public IRubyObject divmod(ThreadContext context, IRubyObject other) { // TODO: full-precision divmod is 1000x slower than MRI! Ruby runtime = context.runtime; RubyBigDecimal val = getVpValue19(context, other, false); @@ -1340,7 +1340,7 @@ public IRubyObject inspect(ThreadContext context) { val.append( getSignificantDigits().length() ).append('('); val.append( ((getAllDigits().length() / 4) + 1) * 4 ).append(')').append('>'); - return getRuntime().newString(val.toString()); + return RubyString.newString(context.runtime, val); } @JRubyMethod(name = "nan?")