Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: jruby/jruby
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: b45f4fea86c0
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: c55a16278903
Choose a head ref
  • 2 commits
  • 7 files changed
  • 2 contributors

Commits on Aug 24, 2015

  1. backport #3271, #3211, #3221

    ahorek committed Aug 24, 2015
    Copy the full SHA
    ae7c197 View commit details
  2. Copy the full SHA
    c55a162 View commit details
Original file line number Diff line number Diff line change
@@ -42,5 +42,8 @@
public class BigDecimalLibrary implements Library {
public void load(Ruby runtime, boolean wrap) throws IOException {
RubyBigDecimal.createBigDecimal(runtime);

// using load since this file does not exist in MRI
runtime.getLoadService().load("jruby/bigdecimal.rb", false);
}
}// BigDecimalLibrary
148 changes: 107 additions & 41 deletions core/src/main/java/org/jruby/ext/bigdecimal/RubyBigDecimal.java
Original file line number Diff line number Diff line change
@@ -163,6 +163,7 @@ public RubyBigDecimal(Ruby runtime, RubyClass klass) {
this.isNaN = false;
this.infinitySign = 0;
this.zeroSign = 0;
this.value = BigDecimal.ZERO;
}

public RubyBigDecimal(Ruby runtime, BigDecimal value) {
@@ -480,6 +481,9 @@ private static RubyBigDecimal getVpValue(ThreadContext context, IRubyObject valu
if (value instanceof RubyFixnum || value instanceof RubyBignum) {
return newInstance(context, context.runtime.getClass("BigDecimal"), value.asString());
}
if ((value instanceof RubyRational) || (value instanceof RubyFloat)) {
return newInstance(context, context.runtime.getClass("BigDecimal"), value.asString(), RubyFixnum.newFixnum(context.runtime, RubyFloat.DIG));
}
return cannotBeCoerced(context, value, must);
}

@@ -508,33 +512,59 @@ private static RubyBigDecimal newInstance(Ruby runtime, IRubyObject recv, RubyFl
// precision can be no more than float digits
if (mathContext.getPrecision() > RubyFloat.DIG + 1) throw runtime.newArgumentError("precision too large");

return new RubyBigDecimal(runtime, (RubyClass) recv, new BigDecimal(arg.getDoubleValue(), mathContext));
double dblVal = arg.getDoubleValue();
if(Double.isInfinite(dblVal) || Double.isNaN(dblVal)) throw runtime.newFloatDomainError("NaN");

return new RubyBigDecimal(runtime, (RubyClass) recv, new BigDecimal(dblVal, mathContext));
}

private static RubyBigDecimal newInstance(Ruby runtime, IRubyObject recv, RubyBignum arg, MathContext mathContext) {
return new RubyBigDecimal(runtime, (RubyClass) recv, new BigDecimal(arg.getBigIntegerValue(), mathContext));
}

private final static Pattern NUMBER_PATTERN = Pattern.compile("^([+-]?\\d*\\.?\\d*([eE][+-]?)?\\d*).*");
private final static Pattern NUMBER_PATTERN = Pattern.compile("^([+-]?\\d*\\.?\\d*([eE]?)([+-]?\\d*)).*");

private static RubyBigDecimal newInstance(ThreadContext context, IRubyObject recv, IRubyObject arg, MathContext mathContext) {
String strValue = arg.convertToString().toString().trim();

switch ( strValue.length() > 2 ? strValue.charAt(0) : ' ' ) { // do not case length == 1
case 'N' :
if ( "NaN".equals(strValue) ) return newNaN(context.runtime);
case 'I' :
if ( "Infinity".equals(strValue) ) return newInfinity(context.runtime, 1);
case '-' :
if ( "-Infinity".equals(strValue) ) return newInfinity(context.runtime, -1);
case '+' :
if ( "+Infinity".equals(strValue) ) return newInfinity(context.runtime, +1);
int sign = 1;
if(strValue.length() > 0) {
switch (strValue.charAt(0)) {
case '_' :
return newZero(context.runtime, 1); // leading "_" are not allowed
case 'N' :
if ( "NaN".equals(strValue) ) return newNaN(context.runtime);
break;
case 'I' :
if ( "Infinity".equals(strValue) ) return newInfinity(context.runtime, 1);
break;
case '-' :
if ( "-Infinity".equals(strValue) ) return newInfinity(context.runtime, -1);
sign = -1;
break;
case '+' :
if ( "+Infinity".equals(strValue) ) return newInfinity(context.runtime, +1);
break;
}
}

// Convert String to Java understandable format (for BigDecimal).
strValue = strValue.replaceFirst("[dD]", "E"); // 1. MRI allows d and D as exponent separators
strValue = strValue.replaceAll("_", ""); // 2. MRI allows underscores anywhere
strValue = NUMBER_PATTERN.matcher(strValue).replaceFirst("$1"); // 3. MRI ignores the trailing junk

Matcher matcher = NUMBER_PATTERN.matcher(strValue);
strValue = matcher.replaceFirst("$1"); // 3. MRI ignores the trailing junk

String exp = matcher.group(2);
if(!exp.isEmpty()) {
String expValue = matcher.group(3);
if (expValue.isEmpty() || expValue.equals("-") || expValue.equals("+")) {
strValue = strValue.concat("0"); // 4. MRI allows 1E, 1E-, 1E+
} else if (isExponentOutOfRange(expValue)) {
// Handle infinity (Integer.MIN_VALUE + 1) < expValue < Integer.MAX_VALUE
return newInfinity(context.runtime, sign);
}
}

BigDecimal decimal;
try {
@@ -547,11 +577,38 @@ private static RubyBigDecimal newInstance(ThreadContext context, IRubyObject rec
}

// MRI behavior: -0 and +0 are two different things
if (decimal.signum() == 0) return newZero(context.runtime, strValue.matches("^\\s*-.*") ? -1 : 1);
if (decimal.signum() == 0) return newZero(context.runtime, sign);

return new RubyBigDecimal(context.runtime, (RubyClass) recv, decimal);
}

private static boolean isExponentOutOfRange(final String expValue) {
int num = 0;
int sign = 1;
final int len = expValue.length();
final char ch = expValue.charAt(0);
if (ch == '-') {
sign = -1;
} else if (ch != '+') {
num = '0' - ch;
}
int i = 1;
final int max = (sign == 1) ? -Integer.MAX_VALUE : Integer.MIN_VALUE + 1;
final int multmax = max / 10;
while (i < len) {
int d = expValue.charAt(i++) - '0';
if (num < multmax) {
return true;
}
num *= 10;
if (num < (max + d)) {
return true;
}
num -= d;
}
return false;
}

@Deprecated
public static RubyBigDecimal newInstance(IRubyObject recv, IRubyObject[] args) {
final ThreadContext context = recv.getRuntime().getCurrentContext();
@@ -566,7 +623,8 @@ public static RubyBigDecimal newInstance(IRubyObject recv, IRubyObject[] args) {
public static RubyBigDecimal newInstance(ThreadContext context, IRubyObject recv, IRubyObject arg) {
if ( context.runtime.is1_9() ) {
if (arg instanceof RubyBigDecimal) return newInstance(context.runtime, recv, (RubyBigDecimal) arg);
if (arg instanceof RubyFloat || arg instanceof RubyRational) throw context.runtime.newArgumentError("can't omit precision for a rational");
if (arg instanceof RubyRational) throw context.runtime.newArgumentError("can't omit precision for a Rational.");
if (arg instanceof RubyFloat) throw context.runtime.newArgumentError("can't omit precision for a Float.");
if (arg instanceof RubyFixnum) return newInstance(context.runtime, recv, (RubyFixnum) arg, MathContext.UNLIMITED);
if (arg instanceof RubyBignum) return newInstance(context.runtime, recv, (RubyBignum) arg, MathContext.UNLIMITED);
}
@@ -1300,9 +1358,6 @@ public IRubyObject coerce(IRubyObject other) {

@JRubyMethod(name = "coerce", required = 1)
public RubyArray coerce(ThreadContext context, IRubyObject other) {
if (other instanceof RubyFloat) {
return context.runtime.newArray(other, to_f());
}
return context.runtime.newArray(getVpValue(context, other, true), this);
}

@@ -1606,31 +1661,11 @@ private static RoundingMode javaRoundingModeFromRubyRoundingMode(Ruby runtime, I

@JRubyMethod(name = "sign")
public IRubyObject sign() {
if (isNaN()) {
return getMetaClass().getConstant("SIGN_NaN");
}

if (isInfinity()) {
if (infinitySign < 0) {
return getMetaClass().getConstant("SIGN_NEGATIVE_INFINITE");
} else {
return getMetaClass().getConstant("SIGN_POSITIVE_INFINITE");
}
}

if (isZero()) {
if (zeroSign < 0) {
return getMetaClass().getConstant("SIGN_NEGATIVE_ZERO");
} else {
return getMetaClass().getConstant("SIGN_POSITIVE_ZERO");
}
}
if (isNaN()) return getMetaClass().getConstant("SIGN_NaN");
if (isInfinity()) return getMetaClass().getConstant(infinitySign < 0 ? "SIGN_NEGATIVE_INFINITE" : "SIGN_POSITIVE_INFINITE");
if (isZero()) return getMetaClass().getConstant(zeroSign < 0 ? "SIGN_NEGATIVE_ZERO" : "SIGN_POSITIVE_ZERO");

if (value.signum() < 0) {
return getMetaClass().getConstant("SIGN_NEGATIVE_FINITE");
} else {
return getMetaClass().getConstant("SIGN_POSITIVE_FINITE");
}
return getMetaClass().getConstant(value.signum() < 0 ? "SIGN_NEGATIVE_FINITE" : "SIGN_POSITIVE_FINITE");
}

@JRubyMethod(name = "split")
@@ -1764,6 +1799,37 @@ public IRubyObject to_int() {
}
}

@JRubyMethod(name = "to_r")
public IRubyObject to_r(ThreadContext context) {
checkFloatDomain();

RubyArray i = split();
long sign = ((Long)i.get(0));
String digits = (String)i.get(1).toString();
long base = ((Long)i.get(2));
long power = ((Long)i.get(3));
long denomi_power = power - digits.length();

IRubyObject bigDigits = RubyBignum.newBignum(getRuntime(), (String)digits).op_mul(context, sign);
RubyBignum numerator;
if(bigDigits instanceof RubyBignum) {
numerator = (RubyBignum)bigDigits;
}
else {
numerator = RubyBignum.newBignum(getRuntime(), bigDigits.toString());
}
IRubyObject num, den;
if(denomi_power < 0) {
num = numerator;
den = RubyFixnum.newFixnum(getRuntime(), base).op_mul(context, RubyFixnum.newFixnum(getRuntime(), -denomi_power));
}
else {
num = numerator.op_pow(context, RubyFixnum.newFixnum(getRuntime(), base).op_mul(context, RubyFixnum.newFixnum(getRuntime(), denomi_power)));
den = RubyFixnum.newFixnum(getRuntime(), 1);
}
return RubyRational.newInstance(context, context.runtime.getRational(), num, den);
}

@JRubyMethod(name = {"to_i", "to_int"}, compat = CompatVersion.RUBY1_9)
public IRubyObject to_int19() {
checkFloatDomain();
70 changes: 52 additions & 18 deletions core/src/main/java/org/jruby/util/ConvertBytes.java
Original file line number Diff line number Diff line change
@@ -119,7 +119,7 @@ public static final ByteList longToByteList(long i, int radix) {

public static final ByteList longToByteList(long i, int radix, byte[] digitmap) {
if (i == 0) return new ByteList(ZERO_BYTES);

if (i == Long.MIN_VALUE) return new ByteList(MIN_VALUE_BYTES[radix]);

boolean neg = false;
@@ -137,7 +137,7 @@ public static final ByteList longToByteList(long i, int radix, byte[] digitmap)
buf[--pos] = digitmap[(int)(i % radix)];
} while ((i /= radix) > 0);
if (neg) buf[--pos] = (byte)'-';

return new ByteList(buf, pos, len - pos);
}

@@ -333,7 +333,7 @@ public static byte[] bytesToUUIDBytes(byte[] randBytes, boolean upper) {
private byte convertDigit(byte c) {
if(c < 0) {
return -1;
}
}
return conv_digit[c];
}

@@ -552,7 +552,7 @@ private long stringToLong(int nptr, int[] endptr, int base) {
i += c;
}
}

if(s != save) {
if(endptr != null) {
endptr[0] = s;
@@ -587,7 +587,7 @@ public RubyInteger byteListToInum() {
}
return runtime.newFixnum(0);
}

ignoreLeadingWhitespace();

boolean sign = getSign();
@@ -599,8 +599,8 @@ public RubyInteger byteListToInum() {
}
return runtime.newFixnum(0);
}
}
}

figureOutBase();

int len = calculateLength();
@@ -643,7 +643,7 @@ public RubyInteger byteListToInum() {
invalidString("Integer"); // trailing garbage
}
}

if(sign) {
return runtime.newFixnum(val);
} else {
@@ -688,6 +688,8 @@ private RubyInteger bigParse(int len, boolean sign) {
result[resultIndex++] = (char)cx;
}

if(resultIndex == 0) { return runtime.newFixnum(0); }

int tmpStr = str;
if (badcheck) {
// no str-- here because we don't null-terminate strings
@@ -700,16 +702,9 @@ private RubyInteger bigParse(int len, boolean sign) {
}
}

BigInteger z;
if(resultIndex == 0) {
z = BigInteger.ZERO;
} else {
z = new BigInteger(new String(result, 0, resultIndex), base);
}

if(!sign) {
z = z.negate();
}
String s = new String(result, 0, resultIndex);
BigInteger z = (base == 10) ? stringToBig(s) : new BigInteger(s, base);
if(!sign) { z = z.negate(); }

if(badcheck) {
if(_str.getBegin() + 1 < str && data[str-1] == '_') {
@@ -726,6 +721,45 @@ private RubyInteger bigParse(int len, boolean sign) {
return RubyBignum.bignorm(runtime, z);
}

private BigInteger stringToBig(String str) {
str = str.replaceAll("_", "");
int size = str.length();
int nDigits = 512;
if (size < nDigits) { nDigits = size; }

int j = size - 1;
int i = j - nDigits + 1;

BigInteger digits[] = new BigInteger[j / nDigits + 1];

for(int z = 0; j >= 0; z++) {
digits[z] = new BigInteger(str.substring(i, j + 1).trim());
j = i - 1;
i = j - nDigits + 1;
if(i < 0) { i = 0; }
}

BigInteger b10x = BigInteger.TEN.pow(nDigits);
int n = digits.length;
while(n > 1) {
i = 0;
j = 0;
while(i < n / 2) {
digits[i] = digits[j].add(digits[j + 1].multiply(b10x));
i += 1;
j += 2;
}
if(j == n-1) {
digits[i] = digits[j];
i += 1;
}
n = i;
b10x = b10x.multiply(b10x);
}

return digits[0];
}

public static class ERange extends RuntimeException {
public static enum Kind {Overflow, Underflow};
private Kind kind;
113 changes: 113 additions & 0 deletions core/src/main/ruby/jruby/bigdecimal.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
require 'bigdecimal'
require 'bigdecimal/util'

module BigMath
module_function

# call-seq:
# log(decimal, numeric) -> BigDecimal
#
# Computes the natural logarithm of +decimal+ to the specified number of
# digits of precision, +numeric+.
#
# If +decimal+ is zero of negative raise Math::DomainError.
#
# If +decimal+ is positive infinity, returns Infinity.
#
# If +decimal+ is NaN, returns NaN.
#
# BigMath::log(BigMath::E(10), 10).to_s
# #=> "1.000000000000"
#
def log(x, prec)
raise ArgumentError if x.nil? || !x.is_a?(Numeric)
raise Math::DomainError if x.is_a?(Complex) || x <= 0
raise ArgumentError unless (true if Integer(prec) rescue false)
prec = prec.to_i
raise ArgumentError if prec < 1
if x.is_a?(BigDecimal) || x.is_a?(Float)
return BigDecimal::INFINITY if x.infinite?
return BigDecimal::NAN if x.nan?
end
x = x.is_a?(Rational) ? x.to_d(Float::DIG) : x.to_d
return BigDecimal::INFINITY if x.infinite?
return BigDecimal::NAN if x.nan?

# this uses the series expansion of the Arctangh (Arc tangens hyperbolicus)
# http://en.wikipedia.org/wiki/Area_hyperbolic_tangent
# where ln(x) = 2 * artanh ((x - 1) / (x + 1))
# d are the elements in the series (getting smaller and smaller)

rmpd_double_figures = 16 # from MRI ruby
n = prec + rmpd_double_figures

# offset the calculation to the efficient (0.1)...(10) window
expo = x.exponent
use_window = (x > 10) || (expo < 0) # allow up to 10 itself
if use_window
offset = BigDecimal.new("1E#{-expo}")
x = x.mult(offset, n)
end

z = (x - 1).div((x + 1), n)
z2 = z.mult(z, n)
series_sum = z
series_element = z

i = 1
while series_element != 0 do
sum_exponent = series_sum.exponent
element_exponent = series_element.exponent
remaining_precision = n - (sum_exponent - element_exponent).abs
break if remaining_precision < 0
z = z.mult(z2, n)
i += 2
series_element = z.div(i, n)
series_sum += series_element
end

window_result = series_sum * 2

# reset the result back to the original value if needed
if use_window
log10 = log(10, n)
window_result + log10.mult(expo, n)
else
window_result
end
end

def exp(x, prec)
raise ArgumentError if x.nil? || !x.is_a?(Numeric) || x.is_a?(Complex)
raise ArgumentError unless (true if Integer(prec) rescue false)
prec = prec.to_i
raise ArgumentError if prec < 1
return BigDecimal::NAN if x.is_a?(BigDecimal) && x.nan?
return BigDecimal::NAN if x.is_a?(Float) && x.nan?
x = x.is_a?(Rational) ? x.to_d(Float::DIG) : x.to_d
return BigDecimal::NAN if x.nan?
return x.sign > 0 ? BigDecimal::INFINITY : BigDecimal(0, prec) if x.infinite?

df = BigDecimal.double_fig
n = prec + df
one = BigDecimal(1)
x = -x if neg = x.sign < 0

y = one;
d = y;
i = 1;

while d.nonzero? && ((m = n - (y.exponent - d.exponent).abs) > 0)
m = df if m < df
d = d.mult(x, n)
d = d.div(i, m)
y += d
i += 1;
end
if neg
one.div(y, prec)
else
y.round(prec - y.exponent)
end
end
end
75 changes: 3 additions & 72 deletions lib/ruby/1.8/bigdecimal/math.rb
Original file line number Diff line number Diff line change
@@ -30,10 +30,10 @@
#
module BigMath

# Computes the square root of x to the specified number of digits of
# Computes the square root of x to the specified number of digits of
# precision.
#
# BigDecimal.new('2').sqrt(16).to_s
# BigDecimal.new('2').sqrt(16).to_s
# -> "0.14142135623730950488016887242096975E1"
#
def sqrt(x,prec)
@@ -120,60 +120,6 @@ def atan(x, prec)
y
end

# Computes the value of e (the base of natural logarithms) raised to the
# power of x, to the specified number of digits of precision.
#
# If x is infinite or NaN, returns NaN.
#
# BigMath::exp(BigDecimal.new('1'), 10).to_s
# -> "0.271828182845904523536028752390026306410273E1"
def exp(x, prec)
raise ArgumentError, "Zero or negative precision for exp" if prec <= 0
return BigDecimal("NaN") if x.infinite? || x.nan?
n = prec + BigDecimal.double_fig
one = BigDecimal("1")
x1 = one
y = one
d = y
z = one
i = 0
while d.nonzero? && ((m = n - (y.exponent - d.exponent).abs) > 0)
m = BigDecimal.double_fig if m < BigDecimal.double_fig
x1 = x1.mult(x,n)
i += 1
z *= i
d = x1.div(z,m)
y += d
end
y
end

# Computes the natural logarithm of x to the specified number of digits
# of precision.
#
# Returns x if x is infinite or NaN.
#
def log(x, prec)
raise ArgumentError, "Zero or negative argument for log" if x <= 0 || prec <= 0
return x if x.infinite? || x.nan?
one = BigDecimal("1")
two = BigDecimal("2")
n = prec + BigDecimal.double_fig
x = (x - one).div(x + one,n)
x2 = x.mult(x,n)
y = x
d = y
i = one
while d.nonzero? && ((m = n - (y.exponent - d.exponent).abs) > 0)
m = BigDecimal.double_fig if m < BigDecimal.double_fig
x = x2.mult(x,n)
i += two
d = x.div(i,m)
y += d
end
y*two
end

# Computes the value of pi to the specified number of digits of precision.
def PI(prec)
raise ArgumentError, "Zero or negative argument for PI" if prec <= 0
@@ -189,7 +135,6 @@ def PI(prec)

d = one
k = one
w = one
t = BigDecimal("-80")
while d.nonzero? && ((m = n - (pi.exponent - d.exponent).abs) > 0)
m = BigDecimal.double_fig if m < BigDecimal.double_fig
@@ -201,7 +146,6 @@ def PI(prec)

d = one
k = one
w = one
t = BigDecimal("956")
while d.nonzero? && ((m = n - (pi.exponent - d.exponent).abs) > 0)
m = BigDecimal.double_fig if m < BigDecimal.double_fig
@@ -217,19 +161,6 @@ def PI(prec)
# digits of precision.
def E(prec)
raise ArgumentError, "Zero or negative precision for E" if prec <= 0
n = prec + BigDecimal.double_fig
one = BigDecimal("1")
y = one
d = y
z = one
i = 0
while d.nonzero? && ((m = n - (y.exponent - d.exponent).abs) > 0)
m = BigDecimal.double_fig if m < BigDecimal.double_fig
i += 1
z *= i
d = one.div(z,m)
y += d
end
y
BigMath.exp(1, prec)
end
end
17 changes: 1 addition & 16 deletions lib/ruby/1.9/bigdecimal/math.rb
Original file line number Diff line number Diff line change
@@ -160,7 +160,6 @@ def PI(prec)

d = one
k = one
w = one
t = BigDecimal("-80")
while d.nonzero? && ((m = n - (pi.exponent - d.exponent).abs) > 0)
m = BigDecimal.double_fig if m < BigDecimal.double_fig
@@ -172,7 +171,6 @@ def PI(prec)

d = one
k = one
w = one
t = BigDecimal("956")
while d.nonzero? && ((m = n - (pi.exponent - d.exponent).abs) > 0)
m = BigDecimal.double_fig if m < BigDecimal.double_fig
@@ -188,19 +186,6 @@ def PI(prec)
# digits of precision.
def E(prec)
raise ArgumentError, "Zero or negative precision for E" if prec <= 0
n = prec + BigDecimal.double_fig
one = BigDecimal("1")
y = one
d = y
z = one
i = 0
while d.nonzero? && ((m = n - (y.exponent - d.exponent).abs) > 0)
m = BigDecimal.double_fig if m < BigDecimal.double_fig
i += 1
z *= i
d = one.div(z,m)
y += d
end
y
BigMath.exp(1, prec)
end
end
17 changes: 1 addition & 16 deletions lib/ruby/2.0/bigdecimal/math.rb
Original file line number Diff line number Diff line change
@@ -159,7 +159,6 @@ def PI(prec)

d = one
k = one
w = one
t = BigDecimal("-80")
while d.nonzero? && ((m = n - (pi.exponent - d.exponent).abs) > 0)
m = BigDecimal.double_fig if m < BigDecimal.double_fig
@@ -171,7 +170,6 @@ def PI(prec)

d = one
k = one
w = one
t = BigDecimal("956")
while d.nonzero? && ((m = n - (pi.exponent - d.exponent).abs) > 0)
m = BigDecimal.double_fig if m < BigDecimal.double_fig
@@ -187,19 +185,6 @@ def PI(prec)
# digits of precision.
def E(prec)
raise ArgumentError, "Zero or negative precision for E" if prec <= 0
n = prec + BigDecimal.double_fig
one = BigDecimal("1")
y = one
d = y
z = one
i = 0
while d.nonzero? && ((m = n - (y.exponent - d.exponent).abs) > 0)
m = BigDecimal.double_fig if m < BigDecimal.double_fig
i += 1
z *= i
d = one.div(z,m)
y += d
end
y
BigMath.exp(1, prec)
end
end