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: 17d611e4a8df
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: fbb0fb63cae3
Choose a head ref
  • 2 commits
  • 3 files changed
  • 2 contributors

Commits on Aug 4, 2015

  1. big decimal

    ahorek committed Aug 4, 2015
    Copy the full SHA
    a4843e5 View commit details

Commits on Aug 5, 2015

  1. Merge pull request #3221 from ahorek/big_decimal

    big decimal parser
    kares committed Aug 5, 2015
    Copy the full SHA
    fbb0fb6 View commit details
Showing with 63 additions and 15 deletions.
  1. +63 −12 core/src/main/java/org/jruby/ext/bigdecimal/RubyBigDecimal.java
  2. +0 −2 test/mri/excludes/TestBigDecimal.rb
  3. +0 −1 test/mri/excludes/TestBigMath.rb
75 changes: 63 additions & 12 deletions core/src/main/java/org/jruby/ext/bigdecimal/RubyBigDecimal.java
Original file line number Diff line number Diff line change
@@ -167,6 +167,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) {
@@ -499,26 +500,49 @@ private static RubyBigDecimal newInstance(Ruby runtime, IRubyObject recv, RubyBi
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 {
@@ -531,11 +555,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();
2 changes: 0 additions & 2 deletions test/mri/excludes/TestBigDecimal.rb
Original file line number Diff line number Diff line change
@@ -23,9 +23,7 @@
exclude :test_limit, "needs investigation"
exclude :test_marshal, "needs investigation"
exclude :test_new, "needs investigation"
exclude :test_not_equal, "needs investigation"

exclude :test_power, "needs investigation"
exclude :test_power_of_finite_with_zero, "needs investigation"
exclude :test_power_of_nan, "needs investigation"
exclude :test_power_of_negative_infinity, "needs investigation"
1 change: 0 additions & 1 deletion test/mri/excludes/TestBigMath.rb
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
exclude :test_log, "fails with DomainError (#2148)"
exclude :test_sqrt, "needs investigation"