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: 637ca8a48fe6
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 7405e6cc78bb
Choose a head ref
  • 6 commits
  • 3 files changed
  • 1 contributor

Commits on Jul 25, 2017

  1. Copy the full SHA
    c8f8c10 View commit details
  2. Copy the full SHA
    a133cb3 View commit details
  3. Copy the full SHA
    d331d07 View commit details
  4. Fix typo.

    headius committed Jul 25, 2017
    Copy the full SHA
    2c3b3bd View commit details
  5. Copy the full SHA
    afdf0a8 View commit details
  6. Provide a more accurate fixable using Big* logic.

    This is to fix cases like rounding floats near FIXNUM_MAX to an
    integer, which may produce a Bignum of FIXNUM_MAX + 1 versus
    a Fixnum of FIXNUM_MAX. Java's cast from double to long prefers
    the latter, truncating the remainder to FIXNUM_MAX.
    
    Note that the more-accurate Big* logic has the overhead of a Java
    BigDecimal and BigInteger at minimum, so I have tried to limit its
    application to cases around fixnum max and min.
    headius committed Jul 25, 2017
    Copy the full SHA
    7405e6c View commit details
Showing with 64 additions and 33 deletions.
  1. +18 −0 core/src/main/java/org/jruby/RubyBignum.java
  2. +21 −20 core/src/main/java/org/jruby/RubyFloat.java
  3. +25 −13 core/src/main/java/org/jruby/RubyNumeric.java
18 changes: 18 additions & 0 deletions core/src/main/java/org/jruby/RubyBignum.java
Original file line number Diff line number Diff line change
@@ -89,6 +89,11 @@ public static RubyBignum newBignum(Ruby runtime, long value) {
return newBignum(runtime, BigInteger.valueOf(value));
}

/**
* Return a Bignum for the given value, or raise FloatDomainError if it is out of range.
*
* Note this method may return Bignum that are in Fixnum range.
*/
public static RubyBignum newBignum(Ruby runtime, double value) {
try {
return newBignum(runtime, new BigDecimal(value).toBigInteger());
@@ -97,6 +102,19 @@ public static RubyBignum newBignum(Ruby runtime, double value) {
}
}

/**
* Return a Bignum or Fixnum (Integer) for the given value, or raise FloatDomainError if it is out of range.
*
* MRI: rb_dbl2big
*/
public static RubyInteger newBignorm(Ruby runtime, double value) {
try {
return bignorm(runtime, new BigDecimal(value).toBigInteger());
} catch (NumberFormatException nfe) {
throw runtime.newFloatDomainError(Double.toString(value));
}
}

public static RubyBignum newBignum(Ruby runtime, BigInteger value) {
return new RubyBignum(runtime, value);
}
41 changes: 21 additions & 20 deletions core/src/main/java/org/jruby/RubyFloat.java
Original file line number Diff line number Diff line change
@@ -748,32 +748,37 @@ public IRubyObject rationalize(ThreadContext context, IRubyObject[] args) {

final Ruby runtime = context.runtime;
RubyFixnum one = RubyFixnum.one(runtime);
RubyFixnum two = RubyFixnum.two(runtime);

IRubyObject eps, a, b;
if (args.length != 0) {
eps = f_abs(context, args[0]);
a = f_sub(context, this, eps);
b = f_add(context, this, eps);
} else {
IRubyObject flt;
IRubyObject p, q;
long[] exp = new long[1];

// float_decode_internal
double f = frexp(value, exp);
f = ldexp(f, DBL_MANT_DIG);
long n = exp[0] - DBL_MANT_DIG;

RubyInteger rf = RubyBignum.newBignorm(runtime, f);
RubyFixnum rn = RubyFixnum.newFixnum(runtime, n);

if (rf.zero_p(context).isTrue() || fix2int(rn) >= 0) {
return RubyRational.newRationalRaw(runtime, rf.op_lshift(context, rn));
}

IRubyObject rf = RubyNumeric.dbl2ival(runtime, f);
IRubyObject rn = RubyFixnum.newFixnum(runtime, n);
RubyInteger two_times_f, den;

if (f_zero_p(context, rf) || !(f_negative_p(context, rn) || f_zero_p(context, rn)))
return RubyRational.newRationalRaw(runtime, f_lshift(context,rf,rn));
RubyFixnum two = RubyFixnum.two(runtime);
two_times_f = (RubyInteger) two.op_mul(context, rf);
den = (RubyInteger) RubyFixnum.one(runtime).op_lshift(context, RubyFixnum.one(runtime).op_minus(context, n));

a = RubyRational.newRationalRaw(runtime,
f_sub(context,f_mul(context, two, rf),one),
f_lshift(context, one, f_sub(context,one,rn)));
b = RubyRational.newRationalRaw(runtime,
f_add(context,f_mul(context, two, rf),one),
f_lshift(context, one, f_sub(context,one,rn)));
a = RubyRational.newRationalRaw(runtime, two_times_f.op_minus(context, RubyFixnum.one(runtime)), den);
b = RubyRational.newRationalRaw(runtime, two_times_f.op_plus(context, RubyFixnum.one(runtime)), den);
}

if (sites(context).op_equal.call(context, a, a, b).isTrue()) return f_to_r(context, this);
@@ -949,17 +954,10 @@ private IRubyObject roundShared(ThreadContext context, RoundingMode roundingMode
return this;
}

if (Double.isInfinite(value)) {
if (Double.isNaN(value)) {
if (ndigits <= 0) throw context.runtime.newFloatDomainError("NaN");
return this;
}
double binexp;
// Missing binexp values for NaN and (-|+)Infinity. frexp man page just says unspecified.
if (value == 0) {
binexp = 0;
} else {
binexp = Math.ceil(Math.log(value)/Math.log(2));
}

if (ndigits < 0) {
return ((RubyInteger) truncate(context)).round(context, ndigits);
@@ -1026,7 +1024,10 @@ private static double roundHalfDown(double x, double s) {
}

private static double roundHalfUp(double xs) {
return Math.floor((Math.floor((xs + 0.5) * 2.0)) / 2.0);
if (xs < 0.0) {
return Math.round(xs * -1.0) * -1.0;
}
return Math.round(xs);
}

private static double roundHalfEven(double x, double s) {
38 changes: 25 additions & 13 deletions core/src/main/java/org/jruby/RubyNumeric.java
Original file line number Diff line number Diff line change
@@ -56,6 +56,7 @@
import org.jruby.util.ConvertDouble;
import org.jruby.util.TypeConverter;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;

@@ -95,13 +96,6 @@ public IRubyObject allocate(Ruby runtime, RubyClass klass) {

public static final double DBL_EPSILON=2.2204460492503131e-16;

private static IRubyObject convertToNum(double val, Ruby runtime) {
if (val >= (double) RubyFixnum.MAX || val < (double) RubyFixnum.MIN) {
return RubyBignum.newBignum(runtime, val);
}
return RubyFixnum.newFixnum(runtime, (long) val);
}

public RubyNumeric(Ruby runtime, RubyClass metaClass) {
super(runtime, metaClass);
}
@@ -264,7 +258,7 @@ public static IRubyObject dbl2ival(Ruby runtime, double val) {
if (fixable(val)) {
return RubyFixnum.newFixnum(runtime, (long) val);
}
return RubyBignum.newBignum(runtime, val);
return RubyBignum.newBignorm(runtime, val);
}

/** rb_num2dbl and NUM2DBL
@@ -1417,18 +1411,36 @@ public static IRubyObject numFuncall(ThreadContext context, final IRubyObject x,
}

// MRI: macro FIXABLE, RB_FIXABLE
// Note: this should not be NaN or +-Inf
public static boolean fixable(double f) {
return posFixable(f) && negFixable(f);
long l = (long) f;
if (l == RubyFixnum.MIN ||
l == RubyFixnum.MAX){
BigInteger bigint = BigDecimal.valueOf(f).toBigInteger();
return posFixable(bigint) && negFixable(bigint);
} else {
return posFixable(l) && negFixable(l);
}
}

// MRI: macro POSFIXABLE, RB_POSFIXABLE
public static boolean posFixable(BigInteger f) {
return f.compareTo(RubyBignum.LONG_MAX) <= 0;
}

// MRI: macro NEGFIXABLE, RB_NEGFIXABLE
public static boolean negFixable(BigInteger f) {
return f.compareTo(RubyBignum.LONG_MIN) >= 0;
}

// MRI: macro POSFIXABLE, RB_POSFIXABLE
public static boolean posFixable(double f) {
return f <= RubyFixnum.MAX;
public static boolean posFixable(long l) {
return l <= RubyFixnum.MAX;
}

// MRI: macro NEGFIXABLE, RB_NEGFIXABLE
public static boolean negFixable(double f) {
return f >= RubyFixnum.MIN;
public static boolean negFixable(long l) {
return l >= RubyFixnum.MIN;
}

private static class NumFuncall1 implements ThreadContext.RecursiveFunctionEx<CallSite> {