com.upokecenter.numbers.RadixMath Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of numbers Show documentation
Show all versions of numbers Show documentation
A Java library that supports arbitrary-precision binary and decimal floating-point numbers and rational numbers with arbitrary-precision components, and supports arithmetic with these numbers.
package com.upokecenter.numbers;
/*
Written by Peter O.
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
If you like this, you should donate to Peter O.
at: http://peteroupc.github.io/
*/
@SuppressWarnings("deprecation") // certain ERounding values are obsolete
class RadixMath implements IRadixMath {
// Use given exponent
private static final int IntegerModeFixedScale = 1;
// Use flexible exponent
private static final int IntegerModeRegular = 0;
private static final int SafeMin32 = -0x3ffffffe;
private static final int SafeMax32 = 0x3ffffffe;
private static final long SafeMin64 = -0x3ffffffffffffffeL;
private static final long SafeMax64 = 0x3ffffffffffffffeL;
private static final int[] BitMasks = {
0x7fffffff, 0x3fffffff, 0x1fffffff,
0xfffffff, 0x7ffffff, 0x3ffffff, 0x1ffffff,
0xffffff, 0x7fffff, 0x3fffff, 0x1fffff,
0xfffff, 0x7ffff, 0x3ffff, 0x1ffff,
0xffff, 0x7fff, 0x3fff, 0x1fff,
0xfff, 0x7ff, 0x3ff, 0x1ff,
0xff, 0x7f, 0x3f, 0x1f,
0xf, 0x7, 0x3, 0x1,
};
private static final long[] BitMasks64 = {
0x7fffffffffffffffL, 0x3fffffffffffffffL, 0x1fffffffffffffffL,
0xfffffffffffffffL, 0x7ffffffffffffffL, 0x3ffffffffffffffL,
0x1ffffffffffffffL,
0xffffffffffffffL, 0x7fffffffffffffL, 0x3fffffffffffffL,
0x1fffffffffffffL,
0xfffffffffffffL, 0x7ffffffffffffL, 0x3ffffffffffffL, 0x1ffffffffffffL,
0xffffffffffffL, 0x7fffffffffffL, 0x3fffffffffffL, 0x1fffffffffffL,
0xfffffffffffL, 0x7ffffffffffL, 0x3ffffffffffL, 0x1ffffffffffL,
0xffffffffffL, 0x7fffffffffL, 0x3fffffffffL, 0x1fffffffffL,
0xfffffffffL, 0x7ffffffffL, 0x3ffffffffL, 0x1ffffffffL,
0xffffffffL, 0x7fffffff, 0x3fffffff, 0x1fffffff,
0xfffffff, 0x7ffffff, 0x3ffffff, 0x1ffffff,
0xffffff, 0x7fffff, 0x3fffff, 0x1fffff,
0xfffff, 0x7ffff, 0x3ffff, 0x1ffff,
0xffff, 0x7fff, 0x3fff, 0x1fff,
0xfff, 0x7ff, 0x3ff, 0x1ff,
0xff, 0x7f, 0x3f, 0x1f,
0xf, 0x7, 0x3, 0x1,
};
private static final int[] OverflowMaxes = {
2147483647, 214748364, 21474836,
2147483, 214748, 21474, 2147, 214, 21, 2,
};
private static final EInteger ValueMinusOne = EInteger.FromInt32(0).Subtract(EInteger.FromInt64(1));
private static final int[] ValueTenPowers = {
1, 10, 100, 1000, 10000, 100000,
1000000, 10000000, 100000000,
1000000000,
};
private static final long[] OverflowMaxes64 = {
9223372036854775807L, 922337203685477580L,
92233720368547758L, 9223372036854775L,
922337203685477L, 92233720368547L,
9223372036854L, 922337203685L,
92233720368L, 9223372036L,
922337203L, 92233720, 9223372,
922337, 92233, 9223, 922, 92, 9,
};
private static final long[] ValueTenPowers64 = {
1, 10, 100, 1000,
10000, 100000, 1000000,
10000000, 100000000, 1000000000,
10000000000L, 100000000000L,
1000000000000L, 10000000000000L,
100000000000000L, 1000000000000000L,
10000000000000000L, 100000000000000000L,
1000000000000000000L,
};
private final IRadixMathHelper helper;
private final int support;
private final int thisRadix;
// Conservative maximum base-10 radix power for
// TryMultiplyByRadixPower; derived from
// Integer.MAX_VALUE*8/3 (8 is the number of bits in a byte;
// 3 is a conservative estimate of log(10)/log(2).)
private static EInteger valueMaxDigits = EInteger.FromInt64(5726623058L);
public RadixMath(IRadixMathHelper helper) {
this.helper = helper;
this.support = helper.GetArithmeticSupport();
this.thisRadix = helper.GetRadix();
}
public T Add(T thisValue, T other, EContext ctx) {
if ((Object)thisValue == null) {
throw new NullPointerException("thisValue");
}
if ((Object)other == null) {
throw new NullPointerException("other");
}
return this.AddEx(thisValue, other, ctx, false);
}
private FastInteger DigitLengthUpperBoundForBitPrecision(FastInteger prec) {
FastInteger result;
if (this.thisRadix == 2) {
result = prec;
} else {
if (this.thisRadix == 10 && prec.CompareToInt(2135) <= 0) {
int value = (1 + ((prec.ToInt32() * 631305) >> 21));
result = new FastInteger(value);
} else if (this.thisRadix == 10 && prec.CompareToInt(6432162) <= 0) {
// Approximation of ln(2)/ln(10)
int value = 1 + (int)(((long)prec.ToInt32() * 661971961083L) >> 41);
result = new FastInteger(value);
} else {
return this.helper.GetDigitLength(
EInteger.FromInt32(1).ShiftLeft(prec.ToEInteger()).Subtract(1));
}
}
return result;
}
private T AddEx32Bit(
int expcmp,
FastIntegerFixed op1Exponent,
FastIntegerFixed op1Mantissa,
FastIntegerFixed op2Exponent,
FastIntegerFixed op2Mantissa,
FastIntegerFixed resultExponent,
int thisFlags,
int otherFlags,
EContext ctx) {
T retval = null;
if ((expcmp == 0 || (op1Exponent.CanFitInInt32() &&
op2Exponent.CanFitInInt32())) &&
op1Mantissa.CanFitInInt32() && op2Mantissa.CanFitInInt32() &&
(thisFlags & BigNumberFlags.FlagNegative) == (otherFlags &
BigNumberFlags.FlagNegative)) {
int negflag = thisFlags & BigNumberFlags.FlagNegative;
int e1int = 0;
int e2int = 0;
if (expcmp != 0) {
e1int = op1Exponent.ToInt32();
e2int = op2Exponent.ToInt32();
}
int m1, m2;
boolean haveRetval = false;
if (expcmp == 0 || (e1int >= SafeMin32 && e1int <= SafeMax32 &&
e2int >= SafeMin32 && e2int <= SafeMax32)) {
int ediff = (expcmp == 0) ? 0 : ((e1int > e2int) ? (e1int - e2int) :
(e2int - e1int));
int radix = this.thisRadix;
if (expcmp == 0) {
m1 = op1Mantissa.ToInt32();
m2 = op2Mantissa.ToInt32();
if (m2 <= Integer.MAX_VALUE - m1) {
m1 += m2;
retval = this.helper.CreateNewWithFlagsFastInt(
FastIntegerFixed.FromInt32(m1),
resultExponent,
negflag);
haveRetval = true;
}
} else if (ediff <= 9 && radix == 10) {
int power = ValueTenPowers[ediff];
int maxoverflow = OverflowMaxes[ediff];
if (expcmp > 0) {
m1 = op1Mantissa.ToInt32();
m2 = op2Mantissa.ToInt32();
if (m1 == 0) {
retval = this.helper.CreateNewWithFlagsFastInt(
op2Mantissa,
op2Exponent,
otherFlags);
} else if (m1 <= maxoverflow) {
m1 *= power;
if (m2 <= Integer.MAX_VALUE - m1) {
m1 += m2;
retval = this.helper.CreateNewWithFlagsFastInt(
FastIntegerFixed.FromInt32(m1),
resultExponent,
negflag);
haveRetval = true;
}
}
} else {
m1 = op1Mantissa.ToInt32();
m2 = op2Mantissa.ToInt32();
if (m2 == 0) {
retval = this.helper.CreateNewWithFlagsFastInt(
op1Mantissa,
op1Exponent,
thisFlags);
}
if (m2 <= maxoverflow) {
m2 *= power;
if (m1 <= Integer.MAX_VALUE - m2) {
m2 += m1;
retval = this.helper.CreateNewWithFlagsFastInt(
FastIntegerFixed.FromInt32(m2),
resultExponent,
negflag);
haveRetval = true;
}
}
}
} else if (ediff <= 30 && radix == 2) {
int mask = BitMasks[ediff];
if (expcmp > 0) {
m1 = op1Mantissa.ToInt32();
m2 = op2Mantissa.ToInt32();
if (m1 == 0) {
retval = this.helper.CreateNewWithFlagsFastInt(
op2Mantissa,
op2Exponent,
otherFlags);
} else if ((m1 & mask) == m1) {
m1 <<= ediff;
if (m2 <= Integer.MAX_VALUE - m1) {
m1 += m2;
retval = this.helper.CreateNewWithFlagsFastInt(
FastIntegerFixed.FromInt32(m1),
resultExponent,
negflag);
haveRetval = true;
}
}
} else {
m1 = op1Mantissa.ToInt32();
m2 = op2Mantissa.ToInt32();
if (m2 == 0) {
retval = this.helper.CreateNewWithFlagsFastInt(
op1Mantissa,
op1Exponent,
thisFlags);
} else if ((m2 & mask) == m2) {
m2 <<= ediff;
if (m1 <= Integer.MAX_VALUE - m2) {
m2 += m1;
retval = this.helper.CreateNewWithFlagsFastInt(
FastIntegerFixed.FromInt32(m2),
resultExponent,
negflag);
haveRetval = true;
}
}
}
}
}
if (haveRetval) {
if (!IsNullOrSimpleContext(ctx)) {
if (resultExponent.isValueZero() &&
this.IsNullOrInt32FriendlyContext(ctx)) {
return retval;
}
retval = this.RoundToPrecision(retval, ctx);
}
return retval;
}
}
if ((thisFlags & BigNumberFlags.FlagNegative) != 0 &&
(otherFlags & BigNumberFlags.FlagNegative) == 0) {
FastIntegerFixed fftmp;
fftmp = op1Exponent;
op1Exponent = op2Exponent;
op2Exponent = fftmp;
fftmp = op1Mantissa;
op1Mantissa = op2Mantissa;
op2Mantissa = fftmp;
int tmp;
tmp = thisFlags;
thisFlags = otherFlags;
otherFlags = tmp;
expcmp = -expcmp;
resultExponent = expcmp < 0 ? op1Exponent : op2Exponent;
}
if ((expcmp == 0 || (op1Exponent.CanFitInInt32() &&
op2Exponent.CanFitInInt32())) &&
op1Mantissa.CanFitInInt32() && op2Mantissa.CanFitInInt32() &&
(thisFlags & BigNumberFlags.FlagNegative) == 0 &&
(otherFlags & BigNumberFlags.FlagNegative) != 0 &&
!op2Mantissa.isValueZero() && !op1Mantissa.isValueZero()) {
int e1int = 0;
int e2int = 0;
int result = 0;
if (expcmp != 0) {
e1int = op1Exponent.ToInt32();
e2int = op2Exponent.ToInt32();
}
int m1, m2;
boolean haveRetval = false;
if (expcmp == 0 || (e1int >= SafeMin32 && e1int <= SafeMax32 &&
e2int >= SafeMin32 && e2int <= SafeMax32)) {
int ediff = (expcmp == 0) ? 0 : ((e1int > e2int) ? (e1int - e2int) :
(e2int - e1int));
int radix = this.thisRadix;
if (expcmp == 0) {
m1 = op1Mantissa.ToInt32();
m2 = op2Mantissa.ToInt32();
if (Integer.MIN_VALUE + m2 <= m1 && m1 >= m2) {
m1 -= m2;
result = m1;
retval = this.helper.CreateNewWithFlagsFastInt(
FastIntegerFixed.FromInt32(m1),
resultExponent,
0);
haveRetval = true;
}
} else if (radix == 10 && ediff <= 9) {
int power = ValueTenPowers[ediff];
int maxoverflow = OverflowMaxes[ediff];
m1 = op1Mantissa.ToInt32();
m2 = op2Mantissa.ToInt32();
boolean negbit = false;
boolean multed = false;
if (expcmp < 0) {
if (m2 <= maxoverflow) {
m2 *= power;
multed = true;
}
} else {
if (m1 <= maxoverflow) {
m1 *= power;
multed = true;
}
}
if (multed && Integer.MIN_VALUE + m2 <= m1) {
m1 -= m2;
if (m1 != Integer.MIN_VALUE) {
negbit = m1 < 0;
result = Math.abs(m1);
retval = this.helper.CreateNewWithFlagsFastInt(
FastIntegerFixed.FromInt32(result),
resultExponent,
negbit ? BigNumberFlags.FlagNegative : 0);
haveRetval = true;
}
}
}
}
if (haveRetval && result != 0) {
if (!IsNullOrSimpleContext(ctx)) {
if (resultExponent.isValueZero() &&
this.IsNullOrInt32FriendlyContext(ctx)) {
return retval;
}
retval = this.RoundToPrecision(retval, ctx);
}
return retval;
}
if (haveRetval && result == 0) {
if (resultExponent.isValueZero() &&
this.IsNullOrInt32FriendlyContext(ctx)) {
return retval;
}
// System.out.println("haveRetval, result=0, [" + thisFlags + "," +
// op1Mantissa + "," + op1Exponent + "] + [" + otherFlags + "," +
// op2Mantissa + "," + op2Exponent + "], retval="+retval);
}
}
return null;
}
private T AddEx64Bit(
long expcmp,
FastIntegerFixed op1Exponent,
FastIntegerFixed op1Mantissa,
FastIntegerFixed op2Exponent,
FastIntegerFixed op2Mantissa,
FastIntegerFixed resultExponent,
int thisFlags,
int otherFlags,
EContext ctx) {
T retval = null;
if ((expcmp == 0 || (op1Exponent.CanFitInInt64() &&
op2Exponent.CanFitInInt64())) &&
op1Mantissa.CanFitInInt64() && op2Mantissa.CanFitInInt64() &&
(thisFlags & BigNumberFlags.FlagNegative) == (otherFlags &
BigNumberFlags.FlagNegative)) {
int negflag = thisFlags & BigNumberFlags.FlagNegative;
long e1long = 0;
long e2long = 0;
if (expcmp != 0) {
e1long = op1Exponent.ToInt64();
e2long = op2Exponent.ToInt64();
}
long m1, m2;
boolean haveRetval = false;
if (expcmp == 0 || (e1long >= SafeMin64 && e1long <= SafeMax64 &&
e2long >= SafeMin64 && e2long <= SafeMax64)) {
long ediffLong = (expcmp == 0) ? 0 : ((e1long > e2long) ?
(e1long - e2long) : (e2long - e1long));
int radix = this.thisRadix;
if (expcmp == 0) {
m1 = op1Mantissa.ToInt64();
m2 = op2Mantissa.ToInt64();
if (m2 <= Long.MAX_VALUE - m1) {
m1 += m2;
retval = this.helper.CreateNewWithFlagsFastInt(
FastIntegerFixed.FromInt64(m1),
resultExponent,
negflag);
haveRetval = true;
}
} else if (ediffLong < ValueTenPowers64.length && radix == 10) {
long power = ValueTenPowers64[(int)ediffLong];
long maxoverflow = OverflowMaxes64[(int)ediffLong];
if (expcmp > 0) {
m1 = op1Mantissa.ToInt64();
m2 = op2Mantissa.ToInt64();
if (m1 == 0) {
retval = this.helper.CreateNewWithFlagsFastInt(
op2Mantissa,
op2Exponent,
otherFlags);
} else if (m1 <= maxoverflow) {
m1 *= power;
if (m2 <= Long.MAX_VALUE - m1) {
m1 += m2;
retval = this.helper.CreateNewWithFlagsFastInt(
FastIntegerFixed.FromInt64(m1),
resultExponent,
negflag);
haveRetval = true;
}
}
} else {
m1 = op1Mantissa.ToInt64();
m2 = op2Mantissa.ToInt64();
if (m2 == 0) {
retval = this.helper.CreateNewWithFlagsFastInt(
op1Mantissa,
op1Exponent,
thisFlags);
}
if (m2 <= maxoverflow) {
m2 *= power;
if (m1 <= Long.MAX_VALUE - m2) {
m2 += m1;
retval = this.helper.CreateNewWithFlagsFastInt(
FastIntegerFixed.FromInt64(m2),
resultExponent,
negflag);
haveRetval = true;
}
}
}
} else if (ediffLong < BitMasks64.length && radix == 2) {
long mask = BitMasks64[(int)ediffLong];
if (expcmp > 0) {
m1 = op1Mantissa.ToInt64();
m2 = op2Mantissa.ToInt64();
if (m1 == 0) {
retval = this.helper.CreateNewWithFlagsFastInt(
op2Mantissa,
op2Exponent,
otherFlags);
} else if ((m1 & mask) == m1) {
m1 <<= (int)ediffLong;
if (m2 <= Long.MAX_VALUE - m1) {
m1 += m2;
retval = this.helper.CreateNewWithFlagsFastInt(
FastIntegerFixed.FromInt64(m1),
resultExponent,
negflag);
haveRetval = true;
}
}
} else {
m1 = op1Mantissa.ToInt64();
m2 = op2Mantissa.ToInt64();
if (m2 == 0) {
retval = this.helper.CreateNewWithFlagsFastInt(
op1Mantissa,
op1Exponent,
thisFlags);
} else if ((m2 & mask) == m2) {
m2 <<= (int)ediffLong;
if (m1 <= Long.MAX_VALUE - m2) {
m2 += m1;
retval = this.helper.CreateNewWithFlagsFastInt(
FastIntegerFixed.FromInt64(m2),
resultExponent,
negflag);
haveRetval = true;
}
}
}
}
}
if (haveRetval) {
if (!IsNullOrSimpleContext(ctx)) {
retval = this.RoundToPrecision(retval, ctx);
}
return retval;
}
}
if ((thisFlags & BigNumberFlags.FlagNegative) != 0 &&
(otherFlags & BigNumberFlags.FlagNegative) == 0) {
FastIntegerFixed fftmp;
fftmp = op1Exponent;
op1Exponent = op2Exponent;
op2Exponent = fftmp;
fftmp = op1Mantissa;
op1Mantissa = op2Mantissa;
op2Mantissa = fftmp;
int tmp;
tmp = thisFlags;
thisFlags = otherFlags;
otherFlags = tmp;
expcmp = -expcmp;
resultExponent = expcmp < 0 ? op1Exponent : op2Exponent;
}
if ((expcmp == 0 || (op1Exponent.CanFitInInt64() &&
op2Exponent.CanFitInInt64())) &&
op1Mantissa.CanFitInInt64() && op2Mantissa.CanFitInInt64() &&
(thisFlags & BigNumberFlags.FlagNegative) == 0 &&
(otherFlags & BigNumberFlags.FlagNegative) != 0 &&
!op2Mantissa.isValueZero() && !op1Mantissa.isValueZero()) {
long e1long = 0;
long e2long = 0;
long result = 0;
if (expcmp != 0) {
e1long = op1Exponent.ToInt64();
e2long = op2Exponent.ToInt64();
}
long m1, m2;
boolean haveRetval = false;
if (expcmp == 0 || (e1long >= SafeMin64 && e1long <= SafeMax64 &&
e2long >= SafeMin64 && e2long <= SafeMax64)) {
long ediffLong = (expcmp == 0) ? 0 : ((e1long > e2long) ?
(e1long - e2long) : (e2long - e1long));
int radix = this.thisRadix;
if (expcmp == 0) {
m1 = op1Mantissa.ToInt64();
m2 = op2Mantissa.ToInt64();
if (Long.MIN_VALUE + m2 <= m1 && m1 >= m2) {
m1 -= m2;
result = m1;
retval = this.helper.CreateNewWithFlagsFastInt(
FastIntegerFixed.FromInt64(m1),
resultExponent,
0);
haveRetval = true;
}
} else if (radix == 10 && ediffLong < ValueTenPowers64.length) {
long power = ValueTenPowers64[(int)ediffLong];
long maxoverflow = OverflowMaxes64[(int)ediffLong];
m1 = op1Mantissa.ToInt64();
m2 = op2Mantissa.ToInt64();
boolean negbit = false;
boolean multed = false;
if (expcmp < 0) {
if (m2 <= maxoverflow) {
m2 *= power;
multed = true;
}
} else {
if (m1 <= maxoverflow) {
m1 *= power;
multed = true;
}
}
if (multed && Long.MIN_VALUE + m2 <= m1) {
m1 -= m2;
if (m1 != Long.MIN_VALUE) {
negbit = m1 < 0;
result = Math.abs(m1);
retval = this.helper.CreateNewWithFlagsFastInt(
FastIntegerFixed.FromInt64(result),
resultExponent,
negbit ? BigNumberFlags.FlagNegative : 0);
haveRetval = true;
}
}
}
}
if (haveRetval && result != 0) {
if (!IsNullOrSimpleContext(ctx)) {
retval = this.RoundToPrecision(retval, ctx);
}
return retval;
}
}
return null;
}
public T AddEx(
T thisValue,
T other,
EContext ctx,
boolean roundToOperandPrecision) {
int thisFlags = this.helper.GetFlags(thisValue);
int otherFlags = this.helper.GetFlags(other);
if (((thisFlags | otherFlags) & BigNumberFlags.FlagSpecial) != 0) {
T result = this.HandleNotANumber(thisValue, other, ctx);
if ((Object)result != (Object)null) {
return result;
}
if ((thisFlags & BigNumberFlags.FlagInfinity) != 0) {
if ((otherFlags & BigNumberFlags.FlagInfinity) != 0) {
if ((thisFlags & BigNumberFlags.FlagNegative) != (otherFlags &
BigNumberFlags.FlagNegative)) {
return this.SignalInvalid(ctx);
}
}
return thisValue;
}
if ((otherFlags & BigNumberFlags.FlagInfinity) != 0) {
return other;
}
}
FastIntegerFixed op1Exponent = this.helper.GetExponentFastInt(thisValue);
FastIntegerFixed op2Exponent = this.helper.GetExponentFastInt(other);
FastIntegerFixed op1Mantissa = this.helper.GetMantissaFastInt(thisValue);
FastIntegerFixed op2Mantissa = this.helper.GetMantissaFastInt(other);
int expcmp = op1Exponent.compareTo(op2Exponent);
FastIntegerFixed resultExponent = expcmp < 0 ? op1Exponent : op2Exponent;
T retval = null;
if ((thisFlags & BigNumberFlags.FlagNegative) == 0 &&
(otherFlags & BigNumberFlags.FlagNegative) == 0) {
if (expcmp < 0 && op2Mantissa.isValueZero()) {
return IsNullOrSimpleContext(ctx) ?
thisValue : this.RoundToPrecision(thisValue, ctx);
} else if (expcmp >= 0 && op1Mantissa.isValueZero()) {
return IsNullOrSimpleContext(ctx) ?
other : this.RoundToPrecision(other, ctx);
}
}
if (!roundToOperandPrecision) {
retval = this.AddEx32Bit(
expcmp,
op1Exponent,
op1Mantissa,
op2Exponent,
op2Mantissa,
resultExponent,
thisFlags,
otherFlags,
ctx);
if ((Object)retval != (Object)null) {
return retval;
}
retval = this.AddEx64Bit(
expcmp,
op1Exponent,
op1Mantissa,
op2Exponent,
op2Mantissa,
resultExponent,
thisFlags,
otherFlags,
ctx);
if ((Object)retval != (Object)null) {
return retval;
}
}
if (expcmp == 0) {
retval = this.AddCore2(
op1Mantissa,
op2Mantissa,
op1Exponent,
thisFlags,
otherFlags,
ctx);
if (!IsNullOrSimpleContext(ctx)) {
retval = this.RoundToPrecision(retval, ctx);
}
} else {
retval = this.AddExDiffExp(
op1Exponent,
op1Mantissa,
op2Exponent,
op2Mantissa,
thisFlags,
otherFlags,
ctx,
expcmp,
roundToOperandPrecision);
}
return retval;
}
public int compareTo(T thisValue, T otherValue) {
if (otherValue == null) {
return 1;
}
int thisFlags = this.helper.GetFlags(thisValue);
int otherFlags = this.helper.GetFlags(otherValue);
if (((thisFlags | otherFlags) & BigNumberFlags.FlagSpecial) != 0) {
return CompareToHandleSpecial2(
thisFlags,
otherFlags);
}
return CompareToInternal(thisValue, otherValue, true, this.helper);
}
public T CompareToWithContext(
T thisValue,
T otherValue,
boolean treatQuietNansAsSignaling,
EContext ctx) {
if (otherValue == null) {
return this.SignalInvalid(ctx);
}
T result = this.CompareToHandleSpecial(
thisValue,
otherValue,
treatQuietNansAsSignaling,
ctx);
if ((Object)result != (Object)null) {
return result;
}
int cmp = CompareToInternal(thisValue, otherValue, false, this.helper);
return (cmp == -2) ? this.SignalInvalidWithMessage(
ctx,
"Out of memory ") :
this.ValueOf(this.compareTo(thisValue, otherValue), null);
}
public T Divide(T thisValue, T divisor, EContext ctx) {
return this.DivideInternal(
thisValue,
divisor,
ctx,
IntegerModeRegular,
EInteger.FromInt32(0));
}
public T DivideToExponent(
T thisValue,
T divisor,
EInteger desiredExponent,
EContext ctx) {
if (ctx != null && !ctx.ExponentWithinRange(desiredExponent)) {
return this.SignalInvalidWithMessage(
ctx,
"Exponent not within exponent range: " + desiredExponent);
}
EContext ctx2 = (ctx == null) ?
EContext.ForRounding(ERounding.HalfDown) :
ctx.WithUnlimitedExponents().WithPrecision(0);
T ret = this.DivideInternal(
thisValue,
divisor,
ctx2,
IntegerModeFixedScale,
desiredExponent);
if (!ctx2.getHasMaxPrecision() && this.IsFinite(ret)) {
// If a precision is given, call Quantize to ensure
// that the value fits the precision
ret = this.Quantize(ret, ret, ctx2);
if ((ctx2.getFlags() & EContext.FlagInvalid) != 0) {
ctx2.setFlags(EContext.FlagInvalid);
}
}
if (ctx != null && ctx.getHasFlags()) {
ctx.setFlags(ctx.getFlags()|(ctx2.getFlags()));
}
return ret;
}
public T DivideToIntegerNaturalScale(
T thisValue,
T divisor,
EContext ctx) {
FastInteger desiredScale =
FastInteger.FromBig(this.helper.GetExponent(thisValue))
.SubtractBig(this.helper.GetExponent(divisor));
EContext ctx2 =
EContext.ForRounding(ERounding.Down).WithBigPrecision(ctx == null ?
EInteger.FromInt32(0) : ctx.getPrecision()).WithBlankFlags();
T ret = this.DivideInternal(
thisValue,
divisor,
ctx2,
IntegerModeFixedScale,
EInteger.FromInt32(0));
if ((ctx2.getFlags() & (EContext.FlagInvalid |
EContext.FlagDivideByZero)) != 0) {
if (ctx != null && ctx.getHasFlags()) {
ctx.setFlags(ctx.getFlags()|(EContext.FlagInvalid | EContext.FlagDivideByZero));
}
return ret;
}
boolean neg = (this.helper.GetSign(thisValue) < 0) ^
(this.helper.GetSign(divisor) < 0);
// Now the exponent's sign can only be 0 or positive
if (this.helper.GetMantissa(ret).isZero()) {
// Value is 0, so just change the exponent
// to the preferred one
EInteger dividendExp = this.helper.GetExponent(thisValue);
EInteger divisorExp = this.helper.GetExponent(divisor);
ret = this.helper.CreateNewWithFlags(
EInteger.FromInt32(0),
dividendExp.Subtract(divisorExp),
this.helper.GetFlags(ret));
} else {
if (desiredScale.signum() < 0) {
// Desired scale is negative, shift left
desiredScale.Negate();
EInteger bigmantissa = this.helper.GetMantissa(ret);
bigmantissa = this.TryMultiplyByRadixPower(bigmantissa,
desiredScale);
if (bigmantissa == null) {
return this.SignalInvalidWithMessage(
ctx,
"Result requires too much memory");
}
EInteger exponentDivisor = this.helper.GetExponent(divisor);
ret = this.helper.CreateNewWithFlags(
bigmantissa,
this.helper.GetExponent(thisValue).Subtract(exponentDivisor),
this.helper.GetFlags(ret));
} else if (desiredScale.signum() > 0) {
// Desired scale is positive, shift away zeros
// but not after scale is reached
EInteger bigmantissa = this.helper.GetMantissa(ret);
FastInteger fastexponent =
FastInteger.FromBig(this.helper.GetExponent(ret));
EInteger bigradix = EInteger.FromInt32(this.thisRadix);
while (true) {
if (desiredScale.compareTo(fastexponent) == 0) {
break;
}
EInteger bigrem;
EInteger bigquo;
{
EInteger[] divrem = bigmantissa.DivRem(bigradix);
bigquo = divrem[0];
bigrem = divrem[1];
}
if (!bigrem.isZero()) {
break;
}
bigmantissa = bigquo;
fastexponent.Increment();
}
ret = this.helper.CreateNewWithFlags(
bigmantissa,
fastexponent.ToEInteger(),
this.helper.GetFlags(ret));
}
}
if (ctx != null) {
ret = this.RoundToPrecision(ret, ctx);
}
ret = this.EnsureSign(ret, neg);
return ret;
}
private T SignalUnderflow(EContext ec, boolean negative, boolean
zeroSignificand) {
EInteger eTiny = ec.getEMin().Subtract(ec.getPrecision().Subtract(1));
eTiny = eTiny.Subtract(2); // subtract 2 from proper eTiny to
// trigger underflow (2, rather than 1, because of HalfUp mode)
T ret = this.helper.CreateNewWithFlags(
zeroSignificand ? EInteger.FromInt32(0) : EInteger.FromInt32(1),
eTiny,
negative ? BigNumberFlags.FlagNegative : 0);
// System.out.println(ret+" underflow "+ec);
return this.RoundToPrecision(ret, ec);
}
public T DivideToIntegerZeroScale(
T thisValue,
T divisor,
EContext ctx) {
EContext ctx2 = EContext.ForRounding(ERounding.Down)
.WithBigPrecision(ctx == null ? EInteger.FromInt32(0) :
ctx.getPrecision()).WithBlankFlags();
T ret = this.DivideInternal(
thisValue,
divisor,
ctx2,
IntegerModeFixedScale,
EInteger.FromInt32(0));
if ((ctx2.getFlags() & (EContext.FlagInvalid |
EContext.FlagDivideByZero)) != 0) {
if (ctx.getHasFlags()) {
ctx.setFlags(ctx.getFlags()|(ctx2.getFlags() & (EContext.FlagInvalid |
EContext.FlagDivideByZero)));
}
return ret;
}
if (ctx != null) {
ctx2 = ctx.WithBlankFlags().WithUnlimitedExponents();
ret = this.RoundToPrecision(ret, ctx2);
if ((ctx2.getFlags() & EContext.FlagRounded) != 0) {
return this.SignalInvalid(ctx);
}
}
return ret;
}
private EInteger WorkingDigits(EInteger workingBits) {
int radix = this.thisRadix;
if (radix <= 2) {
{ return workingBits;
}
}
int ibits = NumberUtility.BitLength(radix) - 1;
return workingBits.Divide(ibits).Add(1);
}
public T Exp(T thisValue, EContext ctx) {
return this.Exp(thisValue, ctx, ctx == null ? null : ctx.getPrecision());
}
private T Exp(T thisValue, EContext ctx, EInteger workingPrecision) {
if (ctx == null) {
return this.SignalInvalidWithMessage(ctx, "ctx is null");
}
if (!ctx.getHasMaxPrecision()) {
return this.SignalInvalidWithMessage(
ctx,
"ctx has unlimited precision");
}
int flags = this.helper.GetFlags(thisValue);
if ((flags & BigNumberFlags.FlagSignalingNaN) != 0) {
// NOTE: Returning a signaling NaN is independent of
// rounding mode
return this.SignalingNaNInvalid(thisValue, ctx);
}
if ((flags & BigNumberFlags.FlagQuietNaN) != 0) {
// NOTE: Returning a quiet NaN is independent of
// rounding mode
return this.ReturnQuietNaN(thisValue, ctx);
}
EContext ctxCopy = ctx.WithBlankFlags();
if ((flags & BigNumberFlags.FlagInfinity) != 0) {
if ((flags & BigNumberFlags.FlagNegative) != 0) {
T retval = this.helper.CreateNewWithFlags(
EInteger.FromInt32(0),
EInteger.FromInt32(0),
0);
retval = this.RoundToPrecision(
retval,
ctxCopy);
if (ctx.getHasFlags()) {
ctx.setFlags(ctx.getFlags()|(ctxCopy.getFlags()));
}
return retval;
}
return thisValue;
}
int sign = this.helper.GetSign(thisValue);
T one = this.helper.ValueOf(1);
EInteger guardDigits = this.thisRadix == 2 ?
workingPrecision.Add(10) : EInteger.FromInt32(10);
EContext ctxdiv = SetPrecisionIfLimited(
ctx,
workingPrecision.Add(guardDigits))
.WithRounding(ERounding.HalfEven).WithBlankFlags();
if (sign == 0) {
thisValue = this.RoundToPrecision(one, ctxCopy);
} else if (sign > 0 && this.compareTo(thisValue, one) <= 0) {
T closeToZero = this.Divide(
this.helper.ValueOf(1),
this.helper.ValueOf(0x800),
null);
if (this.IsFinite(closeToZero) &&
this.compareTo(thisValue, closeToZero) <= 0) {
// Call ExpInternal for magnitudes close to 0, to avoid
// issues when thisValue's magnitude is extremely
// close to 0
thisValue = this.ExpInternalVeryCloseToZero(
thisValue,
ctxdiv.getPrecision(),
ctxCopy);
if (ctx.getHasFlags()) {
ctx.setFlags(ctx.getFlags()|(EContext.FlagInexact |
EContext.FlagRounded | ctxCopy.getFlags()));
}
return thisValue;
}
thisValue = this.ExpInternal(
thisValue,
ctxdiv.getPrecision(),
ctxCopy);
if (ctx.getHasFlags()) {
ctx.setFlags(ctx.getFlags()|(EContext.FlagInexact | EContext.FlagRounded));
}
} else if (sign < 0) {
T closeToZero = this.Divide(
this.helper.ValueOf(-1),
this.helper.ValueOf(0x800),
null);
// System.out.println("ctz="+closeToZero+", wp="+
// workingPrecision+
// " ctxp="+ctx.getPrecision());
if (this.IsFinite(closeToZero) &&
this.compareTo(thisValue, closeToZero) >= 0) {
// Call ExpInternal for magnitudes close to 0, to avoid
// issues when thisValue's magnitude is extremely
// close to 0
// System.out.println("very ctx: thisValue="+thisValue);
thisValue = this.ExpInternalVeryCloseToZero(
thisValue,
ctxdiv.getPrecision(),
ctxCopy);
if (ctx.getHasFlags()) {
ctx.setFlags(ctx.getFlags()|(EContext.FlagInexact |
EContext.FlagRounded | ctxCopy.getFlags()));
}
return thisValue;
}
// System.out.println("ordinary: thisValue="+thisValue);
// exp(x) = 1.Divide(exp)(-x) where x<0
T val = this.Exp(this.NegateRaw(thisValue), ctxdiv);
if ((ctxdiv.getFlags() & EContext.FlagOverflow) != 0 ||
!this.IsFinite(val)) {
// Overflow, try again with expanded exponent range
EInteger newMax;
ctxdiv.setFlags(0);
newMax = ctx.getEMax();
EInteger eintExpdiff = ctx.getEMin();
eintExpdiff = newMax.Subtract(eintExpdiff);
newMax = newMax.Add(eintExpdiff);
ctxdiv = ctxdiv.WithBigExponentRange(ctxdiv.getEMin(), newMax);
thisValue = this.Exp(this.NegateRaw(thisValue), ctxdiv);
if ((ctxdiv.getFlags() & EContext.FlagOverflow) != 0) {
// Still overflowed, so trigger underflow
return this.SignalUnderflow(ctx, false, false);
}
} else {
thisValue = val;
}
thisValue = this.Divide(one, thisValue, ctxCopy);
// System.out.println("end= " + thisValue);
if (ctx.getHasFlags()) {
ctx.setFlags(ctx.getFlags()|(EContext.FlagInexact |
EContext.FlagRounded));
}
} else {
T intpart = null;
boolean haveIntPart = false;
if (ctx.getHasExponentRange() && this.thisRadix >= 2 &&
this.thisRadix <= 12 &&
this.compareTo(thisValue, this.helper.ValueOf(10)) > 0) {
// FastInteger[] precBounds = NumberUtility.DigitLengthBounds(
// this.helper, // this.helper.GetMantissa(thisValue));
// Calculated with ceil(ln(radix))+1 (radixes 0 and 1 are
// not used and have entries of 1)
int[] upperDivisors = {
1, 1, 71, 111, 140, 162, 181, 196, 209, 221, 232, 241, 250,
};
// Calculate an upper bound on the overflow threshold
// for exp
EInteger maxexp = ctx.getEMax().Add(ctx.getPrecision());
maxexp = maxexp.Multiply(upperDivisors[this.thisRadix])
.Divide(100).Add(2);
maxexp = EInteger.Max(EInteger.FromInt32(10), maxexp);
T mxe = this.helper.CreateNewWithFlags(
maxexp,
EInteger.FromInt32(0),
0);
if (this.compareTo(thisValue, mxe) > 0) {
// Greater than overflow bound, so this is an overflow
// System.out.println("thisValue > mxe: " + thisValue + " " + mxe);
return this.SignalOverflow(ctx, false);
}
}
if (ctx.getHasExponentRange() &&
this.compareTo(thisValue, this.helper.ValueOf(50000)) > 0) {
// Try to check for overflow quickly
// Do a trial powering using a lower number than e,
// and a power of 50000
this.PowerIntegral(
this.helper.ValueOf(2),
EInteger.FromInt64(50000),
ctxCopy);
if ((ctxCopy.getFlags() & EContext.FlagOverflow) != 0) {
// The trial powering caused overflow, so exp will
// cause overflow as well
return this.SignalOverflow(ctx, false);
}
intpart = this.Quantize(
thisValue,
one,
EContext.ForRounding(ERounding.Down));
if (!this.GetHelper().GetExponent(intpart).isZero()) {
throw new IllegalArgumentException("integer part not zero, as expected");
}
haveIntPart = true;
ctxCopy.setFlags(0);
// Now do the same using the integer part of the operand
// as the power
this.PowerIntegral(
this.helper.ValueOf(2),
this.helper.GetMantissa(intpart),
ctxCopy);
if ((ctxCopy.getFlags() & EContext.FlagOverflow) != 0) {
// The trial powering caused overflow, so exp will
// cause overflow as well
return this.SignalOverflow(ctx, false);
}
ctxCopy.setFlags(0);
}
if (!haveIntPart) {
intpart = this.Quantize(
thisValue,
one,
EContext.ForRounding(ERounding.Down));
if (!this.GetHelper().GetExponent(intpart).isZero()) {
throw new IllegalArgumentException("integer part not zero, as expected");
}
}
T fracpart = this.Add(thisValue, this.NegateRaw(intpart), null);
// System.out.println("fracpart0=" + fracpart);
ctxdiv = SetPrecisionIfLimited(ctxdiv, ctxdiv.getPrecision().Add(guardDigits))
.WithBlankFlags();
fracpart = this.Add(
one,
this.Divide(fracpart, intpart, ctxdiv),
null);
ctxdiv.setFlags(0);
// System.out.println("fracpart1=" + fracpart);
EInteger workingPrec = ctxdiv.getPrecision();
workingPrec = workingPrec.Add(
this.WorkingDigits(EInteger.FromInt32(20)));
// System.out.println("intpart=" + intpart + " wp=" + workingPrec);
thisValue = this.ExpInternal(fracpart, workingPrec, ctxdiv);
// System.out.println("thisValue=" + thisValue);
if ((ctxdiv.getFlags() & EContext.FlagUnderflow) != 0) {
if (ctx.getHasFlags()) {
ctx.setFlags(ctx.getFlags()|(ctxdiv.getFlags()));
}
}
if (ctx.getHasFlags()) {
ctx.setFlags(ctx.getFlags()|(EContext.FlagInexact |
EContext.FlagRounded));
}
thisValue = this.PowerIntegral(
thisValue,
this.helper.GetMantissa(intpart),
ctxCopy);
// System.out.println(" -->" + thisValue);
}
if (ctx.getHasFlags()) {
ctx.setFlags(ctx.getFlags()|(ctxCopy.getFlags()));
}
return thisValue;
}
public IRadixMathHelper GetHelper() {
return this.helper;
}
public T Ln(T thisValue, EContext ctx) {
if (ctx == null) {
return this.SignalInvalidWithMessage(ctx, "ctx is null");
}
if (!ctx.getHasMaxPrecision()) {
return this.SignalInvalidWithMessage(
ctx,
"ctx has unlimited precision");
}
int flags = this.helper.GetFlags(thisValue);
if ((flags & BigNumberFlags.FlagSignalingNaN) != 0) {
// NOTE: Returning a signaling NaN is independent of
// rounding mode
return this.SignalingNaNInvalid(thisValue, ctx);
}
if ((flags & BigNumberFlags.FlagQuietNaN) != 0) {
// NOTE: Returning a quiet NaN is independent of
// rounding mode
return this.ReturnQuietNaN(thisValue, ctx);
}
int sign = this.helper.GetSign(thisValue);
if (sign < 0) {
return this.SignalInvalid(ctx);
}
if ((flags & BigNumberFlags.FlagInfinity) != 0) {
return thisValue;
}
EContext ctxCopy = ctx.WithBlankFlags();
T one = this.helper.ValueOf(1);
ERounding intermedRounding = ERounding.HalfEven;
if (sign == 0) {
return this.helper.CreateNewWithFlags(
EInteger.FromInt32(0),
EInteger.FromInt32(0),
BigNumberFlags.FlagNegative | BigNumberFlags.FlagInfinity);
} else {
int cmpOne = this.compareTo(thisValue, one);
EContext ctxdiv = null;
if (cmpOne == 0) {
// Equal to 1
thisValue = this.RoundToPrecision(
this.helper.CreateNewWithFlags(EInteger.FromInt32(0), EInteger.FromInt32(0), 0),
ctxCopy);
} else if (cmpOne < 0) {
// Less than 1
T quarter = this.Divide(one, this.helper.ValueOf(4), ctxCopy);
FastInteger error;
error = (this.compareTo(thisValue, quarter) < 0) ?
new FastInteger(20) : new FastInteger(10);
EInteger bigError = error.ToEInteger();
ctxdiv = SetPrecisionIfLimited(ctx, ctx.getPrecision().Add(bigError))
.WithRounding(intermedRounding).WithBlankFlags();
T threeQuarters = this.Multiply(
quarter,
this.helper.ValueOf(3),
null);
if (this.compareTo(thisValue, threeQuarters) <= 0) {
// Three quarters or less
FastInteger roots = new FastInteger(0);
// Take square root until this value
// is 3/4 or more
while (this.compareTo(thisValue, threeQuarters) < 0) {
thisValue = this.SquareRoot(
thisValue,
ctxdiv.WithUnlimitedExponents());
// System.out.println("--> " +thisValue);
roots.Increment();
}
for (int i = 0; i < 6; ++i) {
thisValue = this.SquareRoot(
thisValue,
ctxdiv.WithUnlimitedExponents());
// System.out.println("--> " +thisValue);
roots.Increment();
}
// System.out.println("LnInternal AA " +(thisValue as
// EDecimal)?.ToDouble());
thisValue = this.LnInternal(thisValue, ctxdiv.getPrecision(), ctxdiv);
EInteger bigintRoots = PowerOfTwo(roots);
// Multiply back 2^X, where X is the number
// of square root calls
thisValue = this.Multiply(
thisValue,
this.helper.CreateNewWithFlags(bigintRoots, EInteger.FromInt32(0), 0),
ctxCopy);
} else {
T smallfrac = this.Divide(one, this.helper.ValueOf(16), ctxdiv);
T closeToOne = this.Add(one, this.NegateRaw(smallfrac), null);
if (this.compareTo(thisValue, closeToOne) >= 0) {
// This value is close to 1, so use a higher working precision
error = this.helper.GetDigitLength(this.helper.GetMantissa(
thisValue));
error = error.Copy();
error.AddInt(6);
error.AddBig(ctx.getPrecision());
bigError = error.ToEInteger();
// System.out.println("LnInternalCloseToOne B " +(thisValue as
// EDecimal)?.ToDouble());
thisValue = this.LnInternalCloseToOne2(
thisValue,
error.ToEInteger(),
ctxCopy);
} else {
// System.out.println("LnInternal A " +(thisValue as
// EDecimal)?.ToDouble());
thisValue = this.LnInternal(
thisValue,
ctxdiv.getPrecision(),
ctxCopy);
}
}
if (ctx.getHasFlags()) {
ctxCopy.setFlags(ctxCopy.getFlags()|(EContext.FlagInexact));
ctxCopy.setFlags(ctxCopy.getFlags()|(EContext.FlagRounded));
}
} else {
// Greater than 1
T two = this.helper.ValueOf(2);
if (this.compareTo(thisValue, two) >= 0) {
// 2 or greater
FastInteger roots = new FastInteger(0);
FastInteger error;
EInteger bigError;
error = (this.compareTo(thisValue,
this.helper.ValueOf(Integer.MAX_VALUE)) >= 0) ?
new FastInteger(16) : new FastInteger(10);
bigError = error.ToEInteger();
ctxdiv = SetPrecisionIfLimited(ctx, ctx.getPrecision().Add(bigError))
.WithRounding(intermedRounding).WithBlankFlags();
T smallfrac = (ctxdiv.getPrecision().compareTo(400) > 0) ?
this.Divide(one, this.helper.ValueOf(1000000), ctxdiv) :
this.Divide(one, this.helper.ValueOf(60), ctxdiv);
T closeToOne = this.Add(one, smallfrac, null);
// System.out.println("Before Ln " +thisValue);
// Take square root until this value
// is close to 1
while (this.compareTo(thisValue, closeToOne) >= 0) {
thisValue = this.SquareRoot(
thisValue,
ctxdiv.WithUnlimitedExponents());
// System.out.println("--> " +thisValue);
roots.Increment();
}
// Find -Ln(1/thisValue)
// System.out.println("LnInternalCloseToOne C " + thisValue);
thisValue = this.Divide(one, thisValue, ctxdiv);
// System.out.println("LnInternalCloseToOne C " + thisValue);
thisValue = this.LnInternalCloseToOne2(
thisValue,
ctxdiv.getPrecision(),
ctxdiv);
thisValue = this.NegateRaw(thisValue);
// System.out.println("After LnInternal " +thisValue +
// " roots="+roots);
EInteger bigintRoots = PowerOfTwo(roots);
// Multiply back 2^X, where X is the number
// of square root calls
/* System.out.println("After LnInternal Mult " +this.Multiply(
thisValue,
this.helper.CreateNewWithFlags(bigintRoots, EInteger.FromInt32(0), 0),
ctxdiv));
System.out.println("After LnInternal Mult " +this.Multiply(
thisValue,
this.helper.CreateNewWithFlags(bigintRoots, EInteger.FromInt32(0), 0),
ctxCopy.WithRounding(intermedRounding)));
*/ thisValue = this.Multiply(
thisValue,
this.helper.CreateNewWithFlags(bigintRoots, EInteger.FromInt32(0), 0),
ctxCopy);
// System.out.println("After LnInternal Mult " +(thisValue as
// EDecimal)?.ToDouble());
// System.out.println("ctx=" + ctxCopy + " ");
} else {
FastInteger error;
EInteger bigError;
error = new FastInteger(10);
bigError = error.ToEInteger();
ctxdiv = SetPrecisionIfLimited(ctx, ctx.getPrecision().Add(bigError))
.WithRounding(intermedRounding).WithBlankFlags();
T smallfrac = this.Divide(one, this.helper.ValueOf(16), ctxdiv);
T closeToOne = this.Add(one, smallfrac, null);
if (this.compareTo(thisValue, closeToOne) < 0) {
error = this.helper.GetDigitLength(this.helper.GetMantissa(
thisValue));
error = error.Copy();
error.AddInt(6);
error.AddBig(ctx.getPrecision());
// System.out.println("using error precision: " + error + ", " +
// thisValue);
bigError = error.ToEInteger();
// Greater than 1 and close to 1, will require a higher working
// precision
// System.out.println("LnInternalCloseToOne D " +(thisValue as
// EDecimal)?.ToDouble());
thisValue = this.LnInternalCloseToOne2(
thisValue,
error.ToEInteger(),
ctxCopy);
} else {
// Find -Ln(1/thisValue)
thisValue = this.Divide(one, thisValue, ctxdiv);
// System.out.println("LnInternal B " +(thisValue as
// EDecimal)?.ToDouble());
/* // thisValue = this.LnInternal(
// thisValue, // ctxdiv.getPrecision()
//,
ctxCopy); */ thisValue = this.Ln(thisValue, ctxCopy);
thisValue = this.NegateRaw(thisValue);
}
}
if (ctx.getHasFlags()) {
ctxCopy.setFlags(ctxCopy.getFlags()|(EContext.FlagInexact));
ctxCopy.setFlags(ctxCopy.getFlags()|(EContext.FlagRounded));
}
}
}
if (ctx.getHasFlags()) {
ctx.setFlags(ctx.getFlags()|(ctxCopy.getFlags()));
}
return thisValue;
}
public T Max(T a, T b, EContext ctx) {
if (a == null) {
throw new NullPointerException("a");
}
if (b == null) {
throw new NullPointerException("b");
}
// Handle infinity and NaN
T result = this.MinMaxHandleSpecial(a, b, ctx, false, false);
if ((Object)result != (Object)null) {
return result;
}
int cmp = this.compareTo(a, b);
if (cmp != 0) {
return cmp < 0 ? this.RoundToPrecision(b, ctx) :
this.RoundToPrecision(a, ctx);
}
int flagNegA = this.helper.GetFlags(a) & BigNumberFlags.FlagNegative;
return (flagNegA != (this.helper.GetFlags(b) &
BigNumberFlags.FlagNegative)) ? ((flagNegA != 0) ?
this.RoundToPrecision(b, ctx) : this.RoundToPrecision(a, ctx)) :
((flagNegA == 0) ? (this.helper.GetExponent(a).compareTo(
this.helper.GetExponent(
b)) > 0 ? this.RoundToPrecision(a, ctx) :
this.RoundToPrecision(b, ctx)) : (this.helper.GetExponent(
a).compareTo(
this.helper.GetExponent(
b)) > 0 ? this.RoundToPrecision(b, ctx) :
this.RoundToPrecision(a, ctx)));
}
public T MaxMagnitude(T a, T b, EContext ctx) {
if (a == null) {
throw new NullPointerException("a");
}
if (b == null) {
throw new NullPointerException("b");
}
// Handle infinity and NaN
T result = this.MinMaxHandleSpecial(a, b, ctx, false, true);
if ((Object)result != (Object)null) {
return result;
}
int cmp = this.compareTo(this.AbsRaw(a), this.AbsRaw(b));
return (cmp == 0) ? this.Max(a, b, ctx) : ((cmp > 0) ?
this.RoundToPrecision(
a,
ctx) : this.RoundToPrecision(
b,
ctx));
}
public T Min(T a, T b, EContext ctx) {
if (a == null) {
throw new NullPointerException("a");
}
if (b == null) {
throw new NullPointerException("b");
}
// Handle infinity and NaN
T result = this.MinMaxHandleSpecial(a, b, ctx, true, false);
if ((Object)result != (Object)null) {
return result;
}
int cmp = this.compareTo(a, b);
if (cmp != 0) {
return cmp > 0 ? this.RoundToPrecision(b, ctx) :
this.RoundToPrecision(a, ctx);
}
int signANeg = this.helper.GetFlags(a) & BigNumberFlags.FlagNegative;
return (signANeg != (this.helper.GetFlags(b) &
BigNumberFlags.FlagNegative)) ? ((signANeg != 0) ?
this.RoundToPrecision(a, ctx) : this.RoundToPrecision(b, ctx)) :
((signANeg == 0) ? (this.helper.GetExponent(a).compareTo(
this.helper.GetExponent(
b)) > 0 ? this.RoundToPrecision(b, ctx) :
this.RoundToPrecision(a, ctx)) : (this.helper.GetExponent(
a).compareTo(
this.helper.GetExponent(
b)) > 0 ? this.RoundToPrecision(a, ctx) :
this.RoundToPrecision(b, ctx)));
}
public T MinMagnitude(T a, T b, EContext ctx) {
if (a == null) {
throw new NullPointerException("a");
}
if (b == null) {
throw new NullPointerException("b");
}
// Handle infinity and NaN
T result = this.MinMaxHandleSpecial(a, b, ctx, true, true);
if ((Object)result != (Object)null) {
return result;
}
int cmp = this.compareTo(this.AbsRaw(a), this.AbsRaw(b));
return (cmp == 0) ? this.Min(a, b, ctx) : ((cmp < 0) ?
this.RoundToPrecision(
a,
ctx) : this.RoundToPrecision(
b,
ctx));
}
public T Multiply(T thisValue, T other, EContext ctx) {
int thisFlags = this.helper.GetFlags(thisValue);
int otherFlags = this.helper.GetFlags(other);
if (((thisFlags | otherFlags) & BigNumberFlags.FlagSpecial) != 0) {
T result = this.HandleNotANumber(thisValue, other, ctx);
if ((Object)result != (Object)null) {
return result;
}
if ((thisFlags & BigNumberFlags.FlagInfinity) != 0) {
// Attempt to multiply infinity by 0
boolean negflag = ((thisFlags & BigNumberFlags.FlagNegative) != 0) ^
((otherFlags & BigNumberFlags.FlagNegative) != 0);
return ((otherFlags & BigNumberFlags.FlagSpecial) == 0 &&
this.helper.GetMantissa(other).isZero()) ? this.SignalInvalid(
ctx) : this.EnsureSign(
thisValue,
negflag);
}
if ((otherFlags & BigNumberFlags.FlagInfinity) != 0) {
// Attempt to multiply infinity by 0
boolean negflag = ((thisFlags & BigNumberFlags.FlagNegative) != 0) ^
((otherFlags & BigNumberFlags.FlagNegative) != 0);
return ((thisFlags & BigNumberFlags.FlagSpecial) == 0 &&
this.helper.GetMantissa(thisValue).isZero()) ?
this.SignalInvalid(ctx) : this.EnsureSign(other, negflag);
}
}
EInteger bigintOp2 = this.helper.GetExponent(other);
EInteger newexp = this.helper.GetExponent(thisValue).Add(bigintOp2);
EInteger mantissaOp2 = this.helper.GetMantissa(other);
// System.out.println("" + (this.helper.GetMantissa(thisValue)) + "," +
// (this.helper.GetExponent(thisValue)) + " -> " + mantissaOp2 +", " +
// (bigintOp2));
thisFlags = (thisFlags & BigNumberFlags.FlagNegative) ^ (otherFlags &
BigNumberFlags.FlagNegative);
T ret =
this.helper.CreateNewWithFlags(
this.helper.GetMantissa(thisValue).Multiply(mantissaOp2),
newexp,
thisFlags);
if (ctx != null && ctx != EContext.UnlimitedHalfEven) {
ret = this.RoundToPrecision(ret, ctx);
}
return ret;
}
public T MultiplyAndAdd(
T thisValue,
T multiplicand,
T augend,
EContext ctx) {
if (multiplicand == null) {
throw new NullPointerException("multiplicand");
}
if (augend == null) {
throw new NullPointerException("augend");
}
EContext ctx2 = EContext.UnlimitedHalfEven.WithBlankFlags();
T ret = this.MultiplyAddHandleSpecial(
thisValue,
multiplicand,
augend,
ctx);
if ((Object)ret != (Object)null) {
return ret;
}
T product = this.Multiply(thisValue, multiplicand, ctx2);
ret = this.Add(product, augend, ctx);
if (ctx != null && ctx.getHasFlags()) {
ctx.setFlags(ctx.getFlags()|(ctx2.getFlags()));
}
return ret;
}
public T Negate(T value, EContext ctx) {
int flags = this.helper.GetFlags(value);
if ((flags & BigNumberFlags.FlagSignalingNaN) != 0) {
return this.SignalingNaNInvalid(value, ctx);
}
if ((flags & BigNumberFlags.FlagQuietNaN) != 0) {
return this.ReturnQuietNaN(value, ctx);
}
EInteger mant = this.helper.GetMantissa(value);
T zero;
if ((flags & BigNumberFlags.FlagInfinity) == 0 && mant.isZero()) {
// Negate(-0) is treated as subtract(0, -0), which in turn is treated
// as add (0, 0), so that the result is positive 0 since both
// operands to add are positive
// Negate(0) is treated as subtract(0, 0), which in turn is treated
// as add(0, -0), so that the result is positive 0 since both
// operands to add are positive
boolean nonnegative, floor;
nonnegative = (flags & BigNumberFlags.FlagNegative) == 0;
floor = ctx != null && ctx.getRounding() == ERounding.Floor;
if (floor && nonnegative) {
zero = this.helper.CreateNewWithFlags(
mant,
this.helper.GetExponent(value),
flags | BigNumberFlags.FlagNegative);
} else {
zero = this.helper.CreateNewWithFlags(
mant,
this.helper.GetExponent(value),
flags & ~BigNumberFlags.FlagNegative);
}
// System.out.println("" + (// value) + " -> " + zero + " (nonneg=" +
// nonnegative + ", floor=" + floor + ")");
return this.RoundToPrecision(zero, ctx);
}
flags ^= BigNumberFlags.FlagNegative;
T ret = this.helper.CreateNewWithFlags(
mant,
this.helper.GetExponent(value),
flags);
return this.RoundToPrecision(ret, ctx);
}
public T NextMinus(T thisValue, EContext ctx) {
if (ctx == null) {
return this.SignalInvalidWithMessage(ctx, "ctx is null");
}
if (!ctx.getHasMaxPrecision()) {
return this.SignalInvalidWithMessage(
ctx,
"ctx has unlimited precision");
}
if (!ctx.getHasExponentRange()) {
return this.SignalInvalidWithMessage(
ctx,
"doesn't satisfy ctx.getHasExponentRange()");
}
int flags = this.helper.GetFlags(thisValue);
if ((flags & BigNumberFlags.FlagSignalingNaN) != 0) {
return this.SignalingNaNInvalid(thisValue, ctx);
}
if ((flags & BigNumberFlags.FlagQuietNaN) != 0) {
return this.ReturnQuietNaN(thisValue, ctx);
}
if ((flags & BigNumberFlags.FlagInfinity) != 0) {
if ((flags & BigNumberFlags.FlagNegative) != 0) {
return thisValue;
} else {
EInteger bigexp2 = ctx.getEMax();
EInteger bigprec = ctx.getPrecision();
if (ctx.getAdjustExponent()) {
bigexp2 = bigexp2.Add(EInteger.FromInt32(1));
bigexp2 = bigexp2.Subtract(bigprec);
}
EInteger overflowMant = this.TryMultiplyByRadixPower(
EInteger.FromInt32(1),
FastInteger.FromBig(ctx.getPrecision()));
if (overflowMant == null) {
return this.SignalInvalidWithMessage(
ctx,
"Result requires too much memory");
}
overflowMant = overflowMant.Subtract(EInteger.FromInt32(1));
return this.helper.CreateNewWithFlags(overflowMant, bigexp2, 0);
}
}
FastInteger minexp = FastInteger.FromBig(ctx.getEMin());
if (ctx.getAdjustExponent()) {
minexp.SubtractBig(ctx.getPrecision()).Increment();
}
FastInteger bigexp =
FastInteger.FromBig(this.helper.GetExponent(thisValue));
if (bigexp.compareTo(minexp) <= 0) {
// Use a smaller exponent if the input exponent is already
// very small
minexp = bigexp.Copy().SubtractInt(2);
}
T quantum = this.helper.CreateNewWithFlags(
EInteger.FromInt32(1),
minexp.ToEInteger(),
BigNumberFlags.FlagNegative);
EContext ctx2;
ctx2 = ctx.WithRounding(ERounding.Floor);
return this.Add(thisValue, quantum, ctx2);
}
public T NextPlus(T thisValue, EContext ctx) {
if (ctx == null) {
return this.SignalInvalidWithMessage(ctx, "ctx is null");
}
if (!ctx.getHasMaxPrecision()) {
return this.SignalInvalidWithMessage(
ctx,
"ctx has unlimited precision");
}
if (!ctx.getHasExponentRange()) {
return this.SignalInvalidWithMessage(
ctx,
"doesn't satisfy ctx.getHasExponentRange()");
}
int flags = this.helper.GetFlags(thisValue);
if ((flags & BigNumberFlags.FlagSignalingNaN) != 0) {
return this.SignalingNaNInvalid(thisValue, ctx);
}
if ((flags & BigNumberFlags.FlagQuietNaN) != 0) {
return this.ReturnQuietNaN(thisValue, ctx);
}
if ((flags & BigNumberFlags.FlagInfinity) != 0) {
if ((flags & BigNumberFlags.FlagNegative) != 0) {
EInteger bigexp2 = ctx.getEMax();
EInteger bigprec = ctx.getPrecision();
if (ctx.getAdjustExponent()) {
bigexp2 = bigexp2.Add(EInteger.FromInt32(1));
bigexp2 = bigexp2.Subtract(bigprec);
}
EInteger overflowMant = this.TryMultiplyByRadixPower(
EInteger.FromInt32(1),
FastInteger.FromBig(ctx.getPrecision()));
if (overflowMant == null) {
return this.SignalInvalidWithMessage(
ctx,
"Result requires too much memory");
}
overflowMant = overflowMant.Subtract(EInteger.FromInt32(1));
return this.helper.CreateNewWithFlags(
overflowMant,
bigexp2,
BigNumberFlags.FlagNegative);
}
return thisValue;
}
FastInteger minexp = FastInteger.FromBig(ctx.getEMin());
if (ctx.getAdjustExponent()) {
minexp.SubtractBig(ctx.getPrecision()).Increment();
}
FastInteger bigexp =
FastInteger.FromBig(this.helper.GetExponent(thisValue));
if (bigexp.compareTo(minexp) <= 0) {
// Use a smaller exponent if the input exponent is already
// very small
minexp = bigexp.Copy().SubtractInt(2);
}
T quantum = this.helper.CreateNewWithFlags(
EInteger.FromInt32(1),
minexp.ToEInteger(),
0);
EContext ctx2;
T val = thisValue;
ctx2 = ctx.WithRounding(ERounding.Ceiling);
return this.Add(val, quantum, ctx2);
}
public T NextToward(T thisValue, T otherValue, EContext ctx) {
if (ctx == null) {
return this.SignalInvalidWithMessage(ctx, "ctx is null");
}
if (!ctx.getHasMaxPrecision()) {
return this.SignalInvalidWithMessage(
ctx,
"ctx has unlimited precision");
}
if (!ctx.getHasExponentRange()) {
return this.SignalInvalidWithMessage(
ctx,
"doesn't satisfy ctx.getHasExponentRange()");
}
int thisFlags = this.helper.GetFlags(thisValue);
int otherFlags = this.helper.GetFlags(otherValue);
if (((thisFlags | otherFlags) & BigNumberFlags.FlagSpecial) != 0) {
T result = this.HandleNotANumber(thisValue, otherValue, ctx);
if ((Object)result != (Object)null) {
return result;
}
}
EContext ctx2;
int cmp = this.compareTo(thisValue, otherValue);
if (cmp == 0) {
return this.RoundToPrecision(
this.EnsureSign(
thisValue,
(otherFlags & BigNumberFlags.FlagNegative) != 0),
ctx.WithNoFlags());
} else {
if ((thisFlags & BigNumberFlags.FlagInfinity) != 0) {
if ((thisFlags & (BigNumberFlags.FlagInfinity |
BigNumberFlags.FlagNegative)) == (otherFlags &
(BigNumberFlags.FlagInfinity | BigNumberFlags.FlagNegative))) {
// both values are the same infinity
return thisValue;
} else {
EInteger bigexp2 = ctx.getEMax();
EInteger bigprec = ctx.getPrecision();
if (ctx.getAdjustExponent()) {
bigexp2 = bigexp2.Add(EInteger.FromInt32(1));
bigexp2 = bigexp2.Subtract(bigprec);
}
EInteger overflowMant = this.TryMultiplyByRadixPower(
EInteger.FromInt32(1),
FastInteger.FromBig(ctx.getPrecision()));
if (overflowMant == null) {
return this.SignalInvalidWithMessage(
ctx,
"Result requires too much memory");
}
overflowMant = overflowMant.Subtract(EInteger.FromInt32(1));
return this.helper.CreateNewWithFlags(
overflowMant,
bigexp2,
thisFlags & BigNumberFlags.FlagNegative);
}
}
FastInteger minexp = FastInteger.FromBig(ctx.getEMin());
if (ctx.getAdjustExponent()) {
minexp.SubtractBig(ctx.getPrecision()).Increment();
}
FastInteger bigexp =
FastInteger.FromBig(this.helper.GetExponent(thisValue));
if (bigexp.compareTo(minexp) < 0) {
// Use a smaller exponent if the input exponent is already
// very small
minexp = bigexp.Copy().SubtractInt(2);
} else {
// Ensure the exponent is lower than the exponent range
// (necessary to flag underflow correctly)
minexp.SubtractInt(2);
}
T quantum = this.helper.CreateNewWithFlags(
EInteger.FromInt32(1),
minexp.ToEInteger(),
(cmp > 0) ? BigNumberFlags.FlagNegative : 0);
T val = thisValue;
ctx2 = ctx.WithRounding((cmp > 0) ? ERounding.Floor :
ERounding.Ceiling).WithBlankFlags();
val = this.Add(val, quantum, ctx2);
if ((ctx2.getFlags() & (EContext.FlagOverflow |
EContext.FlagUnderflow)) == 0) {
// Don't set flags except on overflow or underflow,
// in accordance with the DecTest test cases
ctx2.setFlags(0);
}
if ((ctx2.getFlags() & EContext.FlagUnderflow) != 0) {
EInteger bigmant = this.helper.GetMantissa(val);
EInteger maxmant = this.TryMultiplyByRadixPower(
EInteger.FromInt32(1),
FastInteger.FromBig(ctx.getPrecision()).Decrement());
if (maxmant == null) {
return this.SignalInvalidWithMessage(
ctx,
"Result requires too much memory");
}
if (bigmant.compareTo(maxmant) >= 0 ||
ctx.getPrecision().compareTo(EInteger.FromInt32(1)) == 0) {
// don't treat max-precision results as having underflowed
ctx2.setFlags(0);
}
}
if (ctx.getHasFlags()) {
ctx.setFlags(ctx.getFlags()|(ctx2.getFlags()));
}
return val;
}
}
public T Pi(EContext ctx) {
if (ctx == null) {
return this.SignalInvalidWithMessage(ctx, "ctx is null");
}
if (!ctx.getHasMaxPrecision()) {
return this.SignalInvalidWithMessage(
ctx,
"ctx has unlimited precision");
}
// Gauss-Legendre algorithm
T a = this.helper.ValueOf(1);
EContext ctxdiv = SetPrecisionIfLimited(
ctx,
ctx.getPrecision().Add(EInteger.FromInt32(10)))
.WithRounding(ERounding.HalfEven);
T two = this.helper.ValueOf(2);
T b = this.Divide(a, this.SquareRoot(two, ctxdiv), ctxdiv);
T four = this.helper.ValueOf(4);
T half = ((this.thisRadix & 1) == 0) ?
this.helper.CreateNewWithFlags(
EInteger.FromInt64(this.thisRadix / 2),
ValueMinusOne,
0) : null;
T t = this.Divide(a, four, ctxdiv);
boolean more = true;
int lastCompare = 0;
int vacillations = 0;
T lastGuess = null;
T guess = null;
EInteger powerTwo = EInteger.FromInt32(1);
while (more) {
lastGuess = guess;
T aplusB = this.Add(a, b, null);
T newA = (half == null) ? this.Divide(aplusB, two, ctxdiv) :
this.Multiply(aplusB, half, null);
T valueAMinusNewA = this.Add(a, this.NegateRaw(newA), null);
if (!a.equals(b)) {
T atimesB = this.Multiply(a, b, ctxdiv);
b = this.SquareRoot(atimesB, ctxdiv);
}
a = newA;
guess = this.Multiply(aplusB, aplusB, null);
guess = this.Divide(guess, this.Multiply(t, four, null), ctxdiv);
T newGuess = guess;
if ((Object)lastGuess != (Object)null) {
int guessCmp = this.compareTo(lastGuess, newGuess);
if (guessCmp == 0) {
more = false;
} else if ((guessCmp > 0 && lastCompare < 0) || (lastCompare > 0 &&
guessCmp < 0)) {
// Guesses are vacillating
++vacillations;
more &= vacillations <= 3;
}
lastCompare = guessCmp;
}
if (more) {
T tmpT = this.Multiply(valueAMinusNewA, valueAMinusNewA, null);
tmpT = this.Multiply(
tmpT,
this.helper.CreateNewWithFlags(powerTwo, EInteger.FromInt32(0), 0),
null);
t = this.Add(t, this.NegateRaw(tmpT), ctxdiv);
powerTwo = powerTwo.ShiftLeft(1);
}
guess = newGuess;
}
return this.RoundToPrecision(guess, ctx);
}
public T Plus(T thisValue, EContext context) {
return this.RoundToPrecisionInternal(
thisValue,
0,
0,
null,
true,
context);
}
public T Power(T thisValue, T pow, EContext ctx) {
T ret = this.HandleNotANumber(thisValue, pow, ctx);
if ((Object)ret != (Object)null) {
return ret;
}
int thisSign = this.helper.GetSign(thisValue);
int powSign = this.helper.GetSign(pow);
int thisFlags = this.helper.GetFlags(thisValue);
int powFlags = this.helper.GetFlags(pow);
if (thisSign == 0 && powSign == 0) {
// Both operands are zero: invalid
return this.SignalInvalid(ctx);
}
if (thisSign < 0 && (powFlags & BigNumberFlags.FlagInfinity) != 0) {
// This value is negative and power is infinity: invalid
return this.SignalInvalid(ctx);
}
if (thisSign > 0 && (thisFlags & BigNumberFlags.FlagInfinity) == 0 &&
(powFlags & BigNumberFlags.FlagInfinity) != 0) {
// Power is infinity and this value is greater than
// zero and not infinity
int cmp = this.compareTo(thisValue, this.helper.ValueOf(1));
if (cmp < 0) {
// Value is less than 1
if (powSign < 0) {
// Power is negative infinity, return positive infinity
return this.helper.CreateNewWithFlags(
EInteger.FromInt32(0),
EInteger.FromInt32(0),
BigNumberFlags.FlagInfinity);
}
// Power is positive infinity, return 0
return this.RoundToPrecision(
this.helper.CreateNewWithFlags(EInteger.FromInt32(0), EInteger.FromInt32(0), 0),
ctx);
}
if (cmp == 0) {
// Extend the precision of the mantissa as much as possible,
// in the special case that this value is 1
return this.ExtendPrecision(this.helper.ValueOf(1), ctx);
}
// Value is greater than 1
if (powSign > 0) {
// Power is positive infinity, return positive infinity
return pow;
}
// Power is negative infinity, return 0
return this.RoundToPrecision(
this.helper.CreateNewWithFlags(EInteger.FromInt32(0), EInteger.FromInt32(0), 0),
ctx);
}
EInteger powExponent = this.helper.GetExponent(pow);
boolean isPowIntegral = powExponent.signum() > 0;
boolean isPowOdd = false;
T powInt = null;
if (!isPowIntegral) {
powInt = this.Quantize(
pow,
this.helper.CreateNewWithFlags(EInteger.FromInt32(0), EInteger.FromInt32(0), 0),
EContext.ForRounding(ERounding.Down));
isPowIntegral = this.compareTo(powInt, pow) == 0;
isPowOdd = !this.helper.GetMantissa(powInt).isEven();
} else {
if (powExponent.equals(EInteger.FromInt32(0))) {
isPowOdd = !this.helper.GetMantissa(powInt).isEven();
} else if (this.thisRadix % 2 == 0) {
// Never odd for even radixes
isPowOdd = false;
} else {
// System.out.println("trying to quantize " + pow);
powInt = this.Quantize(
pow,
this.helper.CreateNewWithFlags(EInteger.FromInt32(0), EInteger.FromInt32(0), 0),
EContext.ForRounding(ERounding.Down));
isPowOdd = !this.helper.GetMantissa(powInt).isEven();
}
}
// System.out.println("pow=" + pow + " powint=" + powInt);
boolean isResultNegative = (thisFlags & BigNumberFlags.FlagNegative) != 0 &&
(powFlags & BigNumberFlags.FlagInfinity) == 0 && isPowIntegral &&
isPowOdd;
if (thisSign == 0 && powSign != 0) {
int infinityFlags = (powSign < 0) ? BigNumberFlags.FlagInfinity : 0;
if (isResultNegative) {
infinityFlags |= BigNumberFlags.FlagNegative;
}
thisValue = this.helper.CreateNewWithFlags(
EInteger.FromInt32(0),
EInteger.FromInt32(0),
infinityFlags);
if ((infinityFlags & BigNumberFlags.FlagInfinity) == 0) {
thisValue = this.RoundToPrecision(thisValue, ctx);
}
return thisValue;
}
if ((!isPowIntegral || powSign < 0) && (ctx == null ||
!ctx.getHasMaxPrecision())) {
// TODO: In next major version, support the case when:
// - ctx is null or has unlimited precision, and
// - thisValue is greater than 0.
// This case is trivial: divide 1 by thisValue^abs(pow).
String ValueOutputMessage =
"ctx is null or has unlimited precision, " +
"and pow's exponent is not an integer or is negative";
return this.SignalInvalidWithMessage(
ctx,
ValueOutputMessage);
}
if (thisSign < 0 && !isPowIntegral) {
return this.SignalInvalid(ctx);
}
if ((thisFlags & BigNumberFlags.FlagInfinity) != 0) {
// This value is infinity
int negflag = isResultNegative ? BigNumberFlags.FlagNegative : 0;
return (powSign > 0) ? this.RoundToPrecision(
this.helper.CreateNewWithFlags(
EInteger.FromInt32(0),
EInteger.FromInt32(0),
negflag | BigNumberFlags.FlagInfinity),
ctx) : ((powSign < 0) ? this.RoundToPrecision(
this.helper.CreateNewWithFlags(
EInteger.FromInt32(0),
EInteger.FromInt32(0),
negflag),
ctx) : this.RoundToPrecision(
this.helper.CreateNewWithFlags(EInteger.FromInt32(1), EInteger.FromInt32(0), 0),
ctx));
}
if (powSign == 0) {
return
this.RoundToPrecision(
this.helper.CreateNewWithFlags(EInteger.FromInt32(1), EInteger.FromInt32(0), 0),
ctx);
}
if (isPowIntegral) {
EInteger signedMant;
// Special case for 1 in certain cases
if (this.compareTo(thisValue, this.helper.ValueOf(1)) == 0 &&
isPowIntegral) {
EInteger thisExponent = this.helper.GetExponent(thisValue);
if (thisExponent.signum() == 0) {
return (!this.IsWithinExponentRangeForPow(pow, ctx)) ?
this.SignalInvalid(ctx) : this.helper.ValueOf(1);
} else if (powExponent.signum() == 0) {
if (!this.IsWithinExponentRangeForPow(pow, ctx)) {
return this.SignalInvalid(ctx);
}
signedMant = this.helper.GetMantissa(powInt).Abs();
return this.PowerIntegral(thisValue, signedMant, ctx);
}
}
// Very high values of pow and a very high exponent
if (powExponent.compareTo(10) > 0 &&
this.compareTo(pow, this.helper.ValueOf(99999999)) > 0) {
EContext ctxCopy = ctx.WithBlankFlags().WithTraps(0);
// System.out.println("changing pow to 9999999*, ctx="+ctxCopy);
// Try doing Power with a smaller value for pow
T result = this.Power(
thisValue,
this.helper.ValueOf(isPowOdd ? 99999999 : 99999998),
ctxCopy);
if ((ctxCopy.getFlags() & EContext.FlagOverflow) != 0) {
// Caused overflow
if (ctx.getHasFlags()) {
ctx.setFlags(ctx.getFlags()|(ctxCopy.getFlags()));
}
return result;
}
}
if ((Object)powInt == (Object)null) {
// System.out.println("no powInt, quantizing "+pow);
powInt = this.Quantize(
pow,
this.helper.CreateNewWithFlags(EInteger.FromInt32(0), EInteger.FromInt32(0), 0),
EContext.ForRounding(ERounding.Down));
}
signedMant = this.helper.GetMantissa(powInt);
if (powSign < 0) {
signedMant = signedMant.Negate();
}
// System.out.println("tv=" + thisValue + " mant=" + signedMant);
return this.PowerIntegral(thisValue, signedMant, ctx);
}
// Special case for 1
if (this.compareTo(thisValue, this.helper.ValueOf(1)) == 0
// && powSign > 0
) {
// System.out.println("Special case 1B");
return (!this.IsWithinExponentRangeForPow(pow, ctx)) ?
this.SignalInvalid(ctx) :
this.ExtendPrecision(this.helper.ValueOf(1), ctx);
}
// Special case for 0.5
if (this.thisRadix == 10 || this.thisRadix == 2) {
T half = (this.thisRadix == 10) ? this.helper.CreateNewWithFlags(
EInteger.FromInt64(5),
ValueMinusOne,
0) : this.helper.CreateNewWithFlags(
EInteger.FromInt32(1),
ValueMinusOne,
0);
if (this.compareTo(pow, half) == 0 &&
this.IsWithinExponentRangeForPow(pow, ctx) &&
this.IsWithinExponentRangeForPow(thisValue, ctx)) {
EContext ctxCopy = ctx.WithBlankFlags();
thisValue = this.SquareRoot(thisValue, ctxCopy);
ctxCopy.setFlags(ctxCopy.getFlags()|(EContext.FlagInexact));
ctxCopy.setFlags(ctxCopy.getFlags()|(EContext.FlagRounded));
if ((ctxCopy.getFlags() & EContext.FlagSubnormal) != 0) {
ctxCopy.setFlags(ctxCopy.getFlags()|(EContext.FlagUnderflow));
}
thisValue = this.ExtendPrecision(thisValue, ctxCopy);
if (ctx.getHasFlags()) {
ctx.setFlags(ctx.getFlags()|(ctxCopy.getFlags()));
}
return thisValue;
}
}
EInteger guardDigits = this.WorkingDigits(EInteger.FromInt32(17));
EContext ctxdiv = SetPrecisionIfLimited(
ctx,
ctx.getPrecision().Add(guardDigits));
if (ctx.getRounding() != ERounding.Ceiling &&
ctx.getRounding() != ERounding.Floor) {
ctxdiv = ctxdiv.WithRounding(ctx.getRounding())
.WithBlankFlags();
} else {
ctxdiv = ctxdiv.WithRounding(ERounding.Up)
.WithBlankFlags();
}
T lnresult = this.Ln(thisValue, ctxdiv);
// System.out.println("rounding="+ctxdiv.getRounding());
// System.out.println("before mul="+lnresult);
lnresult = this.Multiply(lnresult, pow, ctxdiv);
EInteger workingPrecision = ctxdiv.getPrecision();
// Now use original precision and rounding mode
ctxdiv = ctx.WithBlankFlags();
// System.out.println("before exp="+lnresult);
lnresult = this.Exp(lnresult, ctxdiv);
// System.out.println("after exp.="+lnresult);
if ((ctxdiv.getFlags() & (EContext.FlagClamped |
EContext.FlagOverflow)) != 0) {
if (!this.IsWithinExponentRangeForPow(thisValue, ctx)) {
return this.SignalInvalid(ctx);
}
if (!this.IsWithinExponentRangeForPow(pow, ctx)) {
return this.SignalInvalid(ctx);
}
}
if (ctx.getHasFlags()) {
ctx.setFlags(ctx.getFlags()|(ctxdiv.getFlags()));
}
return lnresult;
}
private boolean IsSubnormal(T value, EContext ctx) {
boolean flag = ctx == null || !ctx.getHasMaxPrecision();
boolean result;
if (flag) {
result = false;
} else {
FastInteger fastInteger = FastInteger.FromBig(
this.helper.GetExponent(value));
FastInteger val = FastInteger.FromBig(ctx.getEMin());
boolean adjustExponent = ctx.getAdjustExponent();
if (adjustExponent) {
FastInteger digitLength =
this.helper.GetDigitLength(this.helper.GetMantissa(value));
fastInteger.Add(digitLength).SubtractInt(1);
}
result = fastInteger.compareTo(val) < 0;
}
return result;
}
public T Quantize(T thisValue, T otherValue, EContext ctx) {
int thisFlags = this.helper.GetFlags(thisValue);
int otherFlags = this.helper.GetFlags(otherValue);
if (((thisFlags | otherFlags) & BigNumberFlags.FlagSpecial) != 0) {
T result = this.HandleNotANumber(thisValue, otherValue, ctx);
if ((Object)result != (Object)null) {
return result;
}
if (((thisFlags & otherFlags) & BigNumberFlags.FlagInfinity) != 0) {
return this.RoundToPrecision(thisValue, ctx);
}
// At this point, it's only the case that either value
// is infinity
return this.SignalInvalid(ctx);
}
EInteger expOther = this.helper.GetExponent(otherValue);
if (ctx != null && !ctx.ExponentWithinRange(expOther)) {
// System.out.println("exp not within range");
return this.SignalInvalidWithMessage(
ctx,
"Exponent not within exponent range: " + expOther);
}
EContext tmpctx = (ctx == null ?
EContext.ForRounding(ERounding.HalfEven) :
ctx.Copy()).WithBlankFlags();
EInteger mantThis = this.helper.GetMantissa(thisValue);
EInteger expThis = this.helper.GetExponent(thisValue);
int expcmp = expThis.compareTo(expOther);
int negativeFlag = this.helper.GetFlags(thisValue) &
BigNumberFlags.FlagNegative;
T ret = null;
if (expcmp == 0) {
// System.out.println("exp same");
ret = this.RoundToPrecision(thisValue, tmpctx);
} else if (mantThis.isZero()) {
// System.out.println("mant is 0");
ret = this.helper.CreateNewWithFlags(
EInteger.FromInt32(0),
expOther,
negativeFlag);
ret = this.RoundToPrecision(ret, tmpctx);
} else if (expcmp > 0) {
// Other exponent is less
// System.out.println("other exp less");
FastInteger radixPower =
FastInteger.FromBig(expThis).SubtractBig(expOther);
if (tmpctx.getPrecision().signum() > 0 &&
radixPower.compareTo(FastInteger.FromBig(tmpctx.getPrecision())
.AddInt(10)) > 0) {
// Radix power is much too high for the current precision
// System.out.println("result too high for prec:" +
// tmpctx.getPrecision() + " radixPower= " + radixPower);
return this.SignalInvalidWithMessage(
ctx,
"Result too high for current precision");
}
mantThis = this.TryMultiplyByRadixPower(mantThis, radixPower);
if (mantThis == null) {
return this.SignalInvalidWithMessage(
ctx,
"Result requires too much memory");
}
ret = this.helper.CreateNewWithFlags(mantThis, expOther, negativeFlag);
ret = this.RoundToPrecision(ret, tmpctx);
} else {
// Other exponent is greater
// System.out.println("other exp greater");
FastInteger shift = FastInteger.FromBig(expOther).SubtractBig(
expThis);
ret = this.RoundToPrecisionInternal(
thisValue,
0,
0,
shift,
false,
tmpctx);
}
if ((tmpctx.getFlags() & EContext.FlagOverflow) != 0) {
// System.out.println("overflow occurred");
return this.SignalInvalid(ctx);
}
if (ret == null || !this.helper.GetExponent(ret).equals(expOther)) {
// System.out.println("exp not same "+ret);
return this.SignalInvalid(ctx);
}
ret = this.EnsureSign(ret, negativeFlag != 0);
if (ctx != null && ctx.getHasFlags()) {
int flags = tmpctx.getFlags();
flags &= ~EContext.FlagUnderflow;
boolean flag12 = expcmp < 0 && !this.helper.GetMantissa(ret).isZero() &&
this.IsSubnormal(ret, ctx);
if (flag12) {
flags |= EContext.FlagSubnormal;
}
ctx.setFlags(ctx.getFlags()|(flags));
}
return ret;
}
public T Reduce(T thisValue, EContext ctx) {
return this.ReduceToPrecisionAndIdealExponent(
thisValue,
ctx,
null,
null);
}
public T Remainder(
T thisValue,
T divisor,
EContext ctx,
boolean roundAfterDivide) {
EContext ctx2 = ctx == null ? null : ctx.WithBlankFlags();
T ret = this.RemainderHandleSpecial(thisValue, divisor, ctx2);
if ((Object)ret != (Object)null) {
TransferFlags(ctx, ctx2);
return ret;
}
ret = this.DivideToIntegerZeroScale(
thisValue,
divisor,
roundAfterDivide ? ctx2 : null);
if ((ctx2.getFlags() & EContext.FlagInvalid) != 0) {
return this.SignalInvalid(ctx);
}
ret = this.Add(
thisValue,
this.NegateRaw(this.Multiply(ret, divisor, null)),
ctx2);
ret = this.EnsureSign(
ret,
(this.helper.GetFlags(thisValue) & BigNumberFlags.FlagNegative) != 0);
TransferFlags(
ctx,
ctx2);
return ret;
}
public T RemainderNear(T thisValue, T divisor, EContext ctx) {
EContext ctx2 = ctx == null ?
EContext.ForRounding(ERounding.HalfEven).WithBlankFlags() :
ctx.WithRounding(ERounding.HalfEven).WithBlankFlags();
T ret = this.RemainderHandleSpecial(thisValue, divisor, ctx2);
if ((Object)ret != (Object)null) {
TransferFlags(ctx, ctx2);
return ret;
}
ret = this.DivideInternal(
thisValue,
divisor,
ctx2,
IntegerModeFixedScale,
EInteger.FromInt32(0));
if ((ctx2.getFlags() & EContext.FlagInvalid) != 0) {
return this.SignalInvalid(ctx);
}
ctx2 = ctx2.WithBlankFlags();
ret = this.RoundToPrecision(ret, ctx2);
if ((ctx2.getFlags() & (EContext.FlagRounded |
EContext.FlagInvalid)) != 0) {
return this.SignalInvalid(ctx);
}
ctx2 = ctx == null ? EContext.UnlimitedHalfEven.WithBlankFlags() :
ctx.WithBlankFlags();
T ret2 = this.Add(
thisValue,
this.NegateRaw(this.Multiply(ret, divisor, null)),
ctx2);
if ((ctx2.getFlags() & EContext.FlagInvalid) != 0) {
return this.SignalInvalid(ctx);
}
if (this.helper.GetFlags(ret2) == 0 &&
this.helper.GetMantissa(ret2).isZero()) {
ret2 = this.EnsureSign(
ret2,
(this.helper.GetFlags(thisValue) & BigNumberFlags.FlagNegative) != 0);
}
TransferFlags(
ctx,
ctx2);
return ret2;
}
public T RoundAfterConversion(T thisValue, EContext ctx) {
// System.out.println("RM RoundAfterConversion");
return this.RoundToPrecision(thisValue, ctx);
}
public T RoundToExponentExact(
T thisValue,
EInteger expOther,
EContext ctx) {
if (this.helper.GetExponent(thisValue).compareTo(expOther) >= 0) {
return this.RoundToPrecision(thisValue, ctx);
} else {
EContext pctx = (ctx == null) ? null :
ctx.WithPrecision(0).WithBlankFlags();
T ret = this.Quantize(
thisValue,
this.helper.CreateNewWithFlags(EInteger.FromInt32(1), expOther, 0),
pctx);
if (ctx != null && ctx.getHasFlags()) {
ctx.setFlags(ctx.getFlags()|(pctx.getFlags()));
}
return ret;
}
}
public T RoundToExponentNoRoundedFlag(
T thisValue,
EInteger exponent,
EContext ctx) {
EContext pctx = (ctx == null) ? null : ctx.WithBlankFlags();
T ret = this.RoundToExponentExact(thisValue, exponent, pctx);
if (ctx != null && ctx.getHasFlags()) {
ctx.setFlags(ctx.getFlags()|(pctx.getFlags() & ~(EContext.FlagInexact |
EContext.FlagRounded)));
}
return ret;
}
public T RoundToExponentSimple(
T thisValue,
EInteger expOther,
EContext ctx) {
int thisFlags = this.helper.GetFlags(thisValue);
if ((thisFlags & BigNumberFlags.FlagSpecial) != 0) {
T result = this.HandleNotANumber(thisValue, thisValue, ctx);
if ((Object)result != (Object)null) {
return result;
}
if ((thisFlags & BigNumberFlags.FlagInfinity) != 0) {
return thisValue;
}
}
if (this.helper.GetExponent(thisValue).compareTo(expOther) >= 0) {
return this.RoundToPrecision(thisValue, ctx);
} else {
if (ctx != null && !ctx.ExponentWithinRange(expOther)) {
return this.SignalInvalidWithMessage(
ctx,
"Exponent not within exponent range: " + expOther);
}
FastInteger shift = FastInteger.FromBig(expOther)
.SubtractBig(this.helper.GetExponent(thisValue));
if (shift.signum() == 0 && IsSimpleContext(ctx)) {
return thisValue;
}
EInteger bigmantissa = this.helper.GetMantissa(thisValue);
if (IsSimpleContext(ctx) && ctx.getRounding() == ERounding.Down) {
EInteger shiftedmant = shift.CanFitInInt32() ?
bigmantissa.ShiftRight((int)shift.ToInt32()) :
bigmantissa.ShiftRight(shift.ToEInteger());
return this.helper.CreateNewWithFlags(
shiftedmant,
expOther,
thisFlags);
} else {
IShiftAccumulator accum =
this.helper.CreateShiftAccumulatorWithDigits(bigmantissa, 0, 0);
accum.TruncateOrShiftRight(
shift,
false);
bigmantissa = accum.getShiftedInt();
thisValue = this.helper.CreateNewWithFlags(
bigmantissa,
expOther,
thisFlags);
return this.RoundToPrecisionInternal(
thisValue,
accum.getLastDiscardedDigit(),
accum.getOlderDiscardedDigits(),
null,
false,
ctx);
}
}
}
public T RoundToPrecision(T thisValue, EContext context) {
return this.RoundToPrecisionInternal(
thisValue,
0,
0,
null,
false,
context);
}
public T SquareRoot(T thisValue, EContext ctx) {
if (ctx == null) {
return this.SignalInvalidWithMessage(ctx, "ctx is null");
}
if (!ctx.getHasMaxPrecision()) {
return this.SignalInvalidWithMessage(
ctx,
"ctx has unlimited precision");
}
T ret = this.SquareRootHandleSpecial(thisValue, ctx);
if ((Object)ret != (Object)null) {
return ret;
}
EContext ctxtmp = ctx.WithBlankFlags();
EInteger currentExp = this.helper.GetExponent(thisValue);
EInteger origExp = currentExp;
EInteger idealExp;
idealExp = currentExp;
idealExp = idealExp.Divide(EInteger.FromInt64(2));
if (currentExp.signum() < 0 && !currentExp.isEven()) {
// Round towards negative infinity; BigInteger's
// division operation rounds towards zero
idealExp = idealExp.Subtract(EInteger.FromInt32(1));
}
// System.out.println("curr=" + currentExp + " ideal=" + idealExp);
if (this.helper.GetSign(thisValue) == 0) {
ret = this.RoundToPrecision(
this.helper.CreateNewWithFlags(
EInteger.FromInt32(0),
idealExp,
this.helper.GetFlags(thisValue)),
ctxtmp);
if (ctx.getHasFlags()) {
ctx.setFlags(ctx.getFlags()|(ctxtmp.getFlags()));
}
return ret;
}
EInteger mantissa = this.helper.GetMantissa(thisValue);
FastInteger digitCount = this.helper.GetDigitLength(mantissa);
FastInteger targetPrecision = FastInteger.FromBig(ctx.getPrecision());
FastInteger precision = targetPrecision.Copy().Multiply(2).AddInt(2);
boolean rounded = false;
boolean inexact = false;
if (digitCount.compareTo(precision) < 0) {
FastInteger diff = precision.Copy().Subtract(digitCount);
// System.out.println(diff);
if ((!diff.isEvenNumber()) ^ (!origExp.isEven())) {
diff.Increment();
}
EInteger bigdiff = diff.ToEInteger();
currentExp = currentExp.Subtract(bigdiff);
mantissa = this.TryMultiplyByRadixPower(mantissa, diff);
if (mantissa == null) {
return this.SignalInvalidWithMessage(
ctx,
"Result requires too much memory");
}
}
EInteger[] sr = mantissa.SqrtRem();
digitCount = this.helper.GetDigitLength(sr[0]);
EInteger squareRootRemainder = sr[1];
// System.out.println("I " + mantissa + " -> " + sr[0] + " [target="+
// targetPrecision + "], (zero= " + squareRootRemainder.isZero() +") "
mantissa = sr[0];
if (!squareRootRemainder.isZero()) {
rounded = true;
inexact = true;
}
EInteger oldexp = currentExp;
currentExp = currentExp.ShiftRight(1);
if (oldexp.signum() < 0 && !oldexp.isEven()) {
// Round towards negative infinity; BigInteger's
// division operation rounds towards zero
currentExp = currentExp.Subtract(EInteger.FromInt32(1));
}
T retval = this.helper.CreateNewWithFlags(mantissa, currentExp, 0);
// System.out.println("idealExp= " + idealExp + ", curr" + currentExp
// +" guess= " + mantissa);
retval = this.RoundToPrecisionInternal(
retval,
0,
inexact ? 1 : 0,
null,
false,
ctxtmp);
currentExp = this.helper.GetExponent(retval);
// System.out.println("guess I " + guess + " idealExp=" + idealExp
// +", curr " + currentExp + " clamped= " +
// (ctxtmp.getFlags()&PrecisionContext.FlagClamped));
if ((ctxtmp.getFlags() & EContext.FlagUnderflow) == 0) {
int expcmp = currentExp.compareTo(idealExp);
if (expcmp <= 0 || !this.IsFinite(retval)) {
retval = this.ReduceToPrecisionAndIdealExponent(
retval,
ctx.getHasExponentRange() ? ctxtmp : null,
inexact ? targetPrecision : null,
FastInteger.FromBig(idealExp));
}
}
if (ctx.getHasFlags()) {
if (ctx.getClampNormalExponents() &&
!this.helper.GetExponent(retval).equals(idealExp) && (ctxtmp.getFlags() &
EContext.FlagInexact) == 0) {
ctx.setFlags(ctx.getFlags()|(EContext.FlagClamped));
}
rounded |= (ctxtmp.getFlags() & EContext.FlagOverflow) != 0;
// System.out.println("guess II " + guess);
currentExp = this.helper.GetExponent(retval);
if (rounded) {
ctxtmp.setFlags(ctxtmp.getFlags()|(EContext.FlagRounded));
} else {
if (currentExp.compareTo(idealExp) > 0) {
// Greater than the ideal, treat as rounded anyway
ctxtmp.setFlags(ctxtmp.getFlags()|(EContext.FlagRounded));
} else {
// System.out.println("idealExp= " + idealExp + ", curr" +
// currentExp + " (II)");
ctxtmp.setFlags(ctxtmp.getFlags()&~(EContext.FlagRounded));
}
}
if (inexact) {
ctxtmp.setFlags(ctxtmp.getFlags()|(EContext.FlagRounded));
ctxtmp.setFlags(ctxtmp.getFlags()|(EContext.FlagInexact));
}
ctx.setFlags(ctx.getFlags()|(ctxtmp.getFlags()));
}
return retval;
}
private static int CompareToFast(
int e1int,
int e2int,
int expcmp,
int signA,
FastIntegerFixed op1Mantissa,
FastIntegerFixed op2Mantissa,
int radix) {
int m1, m2;
// System.out.println("" + (// e1int) + " " + e2int + ", expcmp=" +
// expcmp + ", signA=" + signA + ", om=" + op1Mantissa + ", " +
// op2Mantissa);
if (e1int >= SafeMin32 && e1int <= SafeMax32 &&
e2int >= SafeMin32 && e2int <= SafeMax32) {
int ediff = (e1int > e2int) ? (e1int - e2int) : (e2int - e1int);
if (ediff <= 9 && radix == 10) {
int power = ValueTenPowers[ediff];
int maxoverflow = OverflowMaxes[ediff];
if (expcmp > 0) {
m1 = op1Mantissa.ToInt32();
m2 = op2Mantissa.ToInt32();
if (m1 <= maxoverflow) {
m1 *= power;
return (m1 == m2) ? 0 : ((m1 < m2) ? -signA : signA);
}
} else {
m1 = op1Mantissa.ToInt32();
m2 = op2Mantissa.ToInt32();
if (m2 <= maxoverflow) {
m2 *= power;
return (m1 == m2) ? 0 : ((m1 < m2) ? -signA : signA);
}
}
} else if (ediff <= 30 && radix == 2) {
int mask = BitMasks[ediff];
if (expcmp > 0) {
m1 = op1Mantissa.ToInt32();
m2 = op2Mantissa.ToInt32();
if ((m1 & mask) == m1) {
m1 <<= ediff;
return (m1 == m2) ? 0 : ((m1 < m2) ? -signA : signA);
}
} else {
m1 = op1Mantissa.ToInt32();
m2 = op2Mantissa.ToInt32();
if ((m2 & mask) == m2) {
m2 <<= ediff;
return (m1 == m2) ? 0 : ((m1 < m2) ? -signA : signA);
}
}
}
}
return 2;
}
private static int CompareToFast64(
int e1int,
int e2int,
int expcmp,
int signA,
FastIntegerFixed op1Mantissa,
FastIntegerFixed op2Mantissa,
int radix) {
long m1, m2;
// System.out.println("" + (// e1int) + " " + e2int + ", expcmp=" +
// expcmp + ", signA=" + signA + ", om=" + op1Mantissa + ", " +
// op2Mantissa);
if (e1int >= SafeMin32 && e1int <= SafeMax32 &&
e2int >= SafeMin32 && e2int <= SafeMax32) {
long ediffLong = (e1int > e2int) ? (e1int - e2int) : (e2int - e1int);
if (ediffLong <= 18 && radix == 10) {
long power = ValueTenPowers64[(int)ediffLong];
long maxoverflow = OverflowMaxes64[(int)ediffLong];
if (expcmp > 0) {
m1 = op1Mantissa.ToInt64();
m2 = op2Mantissa.ToInt64();
// System.out.println("overflowmax " + maxoverflow + " for " + m1);
if (m1 <= maxoverflow) {
m1 *= power;
return (m1 == m2) ? 0 : ((m1 < m2) ? -signA : signA);
}
} else {
m1 = op1Mantissa.ToInt64();
m2 = op2Mantissa.ToInt64();
// System.out.println("overflowmax " + maxoverflow + " for " + m2);
if (m2 <= maxoverflow) {
m2 *= power;
return (m1 == m2) ? 0 : ((m1 < m2) ? -signA : signA);
}
}
} else if (ediffLong <= 62 && radix == 2) {
long mask = BitMasks64[(int)ediffLong];
if (expcmp > 0) {
m1 = op1Mantissa.ToInt64();
m2 = op2Mantissa.ToInt64();
if ((m1 & mask) == m1) {
m1 <<= (int)ediffLong;
return (m1 == m2) ? 0 : ((m1 < m2) ? -signA : signA);
}
} else {
m1 = op1Mantissa.ToInt64();
m2 = op2Mantissa.ToInt64();
if ((m2 & mask) == m2) {
m2 <<= (int)ediffLong;
return (m1 == m2) ? 0 : ((m1 < m2) ? -signA : signA);
}
}
}
}
return 2;
}
private static int CompareToSlow(
EInteger op1Exponent,
EInteger op2Exponent,
int expcmp,
int signA,
EInteger op1Mantissa,
EInteger op2Mantissa,
IRadixMathHelper helper,
boolean reportOOM) {
long bitExp1 = op1Exponent.GetUnsignedBitLengthAsInt64();
long bitExp2 = op2Exponent.GetUnsignedBitLengthAsInt64();
if (bitExp1 < Long.MAX_VALUE && bitExp2 < Long.MAX_VALUE &&
helper.GetRadix() <= 10 && op1Exponent.signum() == op2Exponent.signum() && (
(bitExp2 > bitExp1 && (bitExp2 - bitExp1) > 128) ||
(bitExp1 > bitExp2 && (bitExp1 - bitExp2) > 128))) {
// Bit difference in two exponents means exponent difference
// is so big that the digit counts of the two significands
// can't keep up (that is, exponent difference is greater than 2^128,
// which is more than the maximum number of bits that
// a significand can currently have).
boolean op2bigger = op1Exponent.signum() < 0 ? (bitExp2 < bitExp1) :
(bitExp2 > bitExp1);
if (op2bigger) {
// operand 2 has greater magnitude
return signA < 0 ? 1 : -1;
} else {
// operand 1 has greater magnitude
return signA < 0 ? -1 : 1;
}
}
FastInteger fastOp1Exp = FastInteger.FromBig(op1Exponent);
FastInteger fastOp2Exp = FastInteger.FromBig(op2Exponent);
FastInteger expdiff = fastOp1Exp.Copy().Subtract(fastOp2Exp).Abs();
// Check if exponent difference is too big for
// radix-power calculation to work quickly
if (expdiff.CompareToInt(200) >= 0) {
EInteger op1MantAbs = op1Mantissa;
EInteger op2MantAbs = op2Mantissa;
FastInteger[] op1DigitBounds =
NumberUtility.DigitLengthBounds(helper, op1MantAbs);
FastInteger[] op2DigitBounds =
NumberUtility.DigitLengthBounds(helper, op2MantAbs);
FastInteger op2ExpUpperBound = fastOp2Exp.Copy().Add(
op2DigitBounds[1]);
FastInteger op1ExpLowerBound = fastOp1Exp.Copy().Add(
op1DigitBounds[0]);
if (op2ExpUpperBound.compareTo(op1ExpLowerBound) < 0) {
// Operand 2's magnitude can't reach highest digit of operand 1,
// meaning operand 1 has a greater magnitude
return signA < 0 ? -1 : 1;
}
FastInteger op1ExpUpperBound = fastOp1Exp.Copy().Add(
op1DigitBounds[1]);
FastInteger op2ExpLowerBound = fastOp2Exp.Copy().Add(
op2DigitBounds[0]);
// System.out.println("1ub="+op1ExpUpperBound +
// " 2lb="+op2ExpLowerBound);
if (op1ExpUpperBound.compareTo(op2ExpLowerBound) < 0) {
// Operand 1's magnitude can't reach highest digit of operand 2,
// meaning operand 2 has a greater magnitude
return signA < 0 ? 1 : -1;
}
FastInteger precision1 =
op1DigitBounds[0].compareTo(op1DigitBounds[1]) == 0 ?
op1DigitBounds[0] : helper.GetDigitLength(op1MantAbs);
FastInteger precision2 =
op2DigitBounds[0].compareTo(op2DigitBounds[1]) == 0 ?
op2DigitBounds[0] : helper.GetDigitLength(op2MantAbs);
FastInteger exp1 = fastOp1Exp.Copy().Add(precision1).Decrement();
FastInteger exp2 = fastOp2Exp.Copy().Add(precision2).Decrement();
int adjcmp = exp1.compareTo(exp2);
if (adjcmp != 0) {
// System.out.println("cmp=" + ((signA < 0) ? -adjcmp : adjcmp));
return (signA < 0) ? -adjcmp : adjcmp;
}
FastInteger maxPrecision = null;
maxPrecision = (precision1.compareTo(precision2) > 0) ? precision1 :
precision2;
// If exponent difference is greater than the
// maximum precision of the two operands
if (expdiff.Copy().compareTo(maxPrecision) > 0) {
int expcmp2 = fastOp1Exp.compareTo(fastOp2Exp);
if (expcmp2 < 0) {
if (!op2MantAbs.isZero()) {
// first operand's exponent is less
// and second operand isn't zero
// second mantissa will be shifted by the exponent
// difference
FastInteger digitLength1 = helper.GetDigitLength(
op1MantAbs);
if (fastOp1Exp.Copy().Add(digitLength1).AddInt(2)
.compareTo(fastOp2Exp) < 0) {
// first operand's mantissa can't reach the
// second operand's mantissa, so the exponent can be
// raised without affecting the result
FastInteger tmp = fastOp2Exp.Copy()
.SubtractInt(8).Subtract(digitLength1).Subtract(
maxPrecision);
FastInteger newDiff = tmp.Copy().Subtract(fastOp2Exp).Abs();
if (newDiff.compareTo(expdiff) < 0) {
// At this point, both operands have the same sign
// System.out.println("cmp case 1=" + ((signA < 0) ? 1 : -1));
return (signA < 0) ? 1 : -1;
}
}
}
} else if (expcmp2 > 0) {
if (!op1MantAbs.isZero()) {
// first operand's exponent is greater
// and second operand isn't zero
// first mantissa will be shifted by the exponent
// difference
FastInteger digitLength2 = helper.GetDigitLength(
op2MantAbs);
if (fastOp2Exp.Copy()
.Add(digitLength2).AddInt(2).compareTo(fastOp1Exp) <
0) {
// second operand's mantissa can't reach the
// first operand's mantissa, so the exponent can be
// raised without affecting the result
FastInteger tmp = fastOp1Exp.Copy()
.SubtractInt(8).Subtract(digitLength2).Subtract(
maxPrecision);
FastInteger newDiff = tmp.Copy().Subtract(fastOp1Exp).Abs();
if (newDiff.compareTo(expdiff) < 0) {
// At this point, both operands have the same sign
// System.out.println("cmp case 2=" + ((signA < 0) ? -1 : 1));
return (signA < 0) ? -1 : 1;
}
}
}
}
expcmp = op1Exponent.compareTo(op2Exponent);
}
// System.out.println("must rescale, expcmp=" + (expcmp));
}
if (expcmp > 0) {
// if ((op1Exponent-op2Exponent).Abs() > 10) {
// System.out.println("" + op1Mantissa + " " + op2Mantissa + " [exp="
// + op1Exponent + " " + op2Exponent + "]");
// }
EInteger newmant = RescaleByExponentDiff(
op1Mantissa,
op1Exponent,
op2Exponent,
helper);
if (newmant == null) {
if (reportOOM) {
throw new OutOfMemoryError("Result requires too much memory");
}
return -2;
}
int mantcmp = newmant.compareTo(op2Mantissa);
return (signA < 0) ? -mantcmp : mantcmp;
} else {
// if ((op1Exponent-op2Exponent).Abs() > 10) {
// System.out.println("" + op1Mantissa + " " + op2Mantissa + " [exp="
// + op1Exponent + " " + op2Exponent + "]");
// }
EInteger newmant = RescaleByExponentDiff(
op2Mantissa,
op1Exponent,
op2Exponent,
helper);
if (newmant == null) {
if (reportOOM) {
throw new OutOfMemoryError("Result requires too much memory");
}
return -2;
}
int mantcmp = op1Mantissa.compareTo(newmant);
return (signA < 0) ? -mantcmp : mantcmp;
}
}
private static boolean IsNullOrSimpleContext(EContext ctx) {
return ctx == null || ctx == EContext.UnlimitedHalfEven ||
(!ctx.getHasExponentRange() && !ctx.getHasMaxPrecision() && ctx.getTraps() == 0 &&
!ctx.getHasFlags());
}
private static boolean IsSimpleContext(EContext ctx) {
return ctx != null && (ctx == EContext.UnlimitedHalfEven ||
(!ctx.getHasExponentRange() && !ctx.getHasMaxPrecision() && ctx.getTraps() == 0 &&
!ctx.getHasFlags()));
}
private static EInteger PowerOfTwo(FastInteger fi) {
if (fi.signum() <= 0) {
return EInteger.FromInt32(1);
}
if (fi.CanFitInInt32()) {
return EInteger.FromInt32(1).ShiftLeft(fi.ToInt32());
} else {
return EInteger.FromInt32(1).ShiftLeft(fi.ToEInteger());
}
}
private static FastIntegerFixed RescaleByExponentDiff(
FastIntegerFixed mantissa,
FastIntegerFixed fe1,
FastIntegerFixed fe2,
IRadixMathHelper helper) {
if (mantissa.signum() == 0) {
return FastIntegerFixed.FromInt32(0);
}
FastIntegerFixed eidiff = fe1.Subtract(fe2).Abs();
if (!eidiff.CanFitInInt32()) {
// NOTE: For radix 10, each digit fits less than 1 byte; the
// supported byte length is thus less than the maximum value
// of a 32-bit integer (2GB).
if (helper.GetRadix() != 10 || eidiff.compareTo(mantissa) > 0) {
return null;
}
}
return helper.MultiplyByRadixPowerFastInt(mantissa, eidiff);
}
private static EInteger RescaleByExponentDiff(
EInteger mantissa,
EInteger e1,
EInteger e2,
IRadixMathHelper helper) {
if (mantissa.signum() == 0) {
return EInteger.FromInt32(0);
}
FastInteger diff = FastInteger.FromBig(e1).SubtractBig(e2).Abs();
if (!diff.CanFitInInt32()) {
// NOTE: For radix 10, each digit fits less than 1 byte; the
// supported byte length is thus less than the maximum value
// of a 32-bit integer (2GB).
FastInteger fastBI = FastInteger.FromBig(mantissa);
if (helper.GetRadix() != 10 || diff.compareTo(fastBI) > 0) {
return null;
}
}
return helper.MultiplyByRadixPower(mantissa, diff);
}
private static EContext SetPrecisionIfLimited(
EContext ctx,
EInteger bigPrecision) {
return (ctx == null || !ctx.getHasMaxPrecision()) ? ctx :
ctx.WithBigPrecision(bigPrecision);
}
private static void TransferFlags(
EContext ctxDst,
EContext ctxSrc) {
if (ctxDst != null && ctxDst.getHasFlags()) {
if ((ctxSrc.getFlags() & (EContext.FlagInvalid |
EContext.FlagDivideByZero)) != 0) {
ctxDst.setFlags(ctxDst.getFlags()|(ctxSrc.getFlags() & (EContext.FlagInvalid |
EContext.FlagDivideByZero)));
} else {
ctxDst.setFlags(ctxDst.getFlags()|(ctxSrc.getFlags()));
}
}
}
public T Abs(T value, EContext ctx) {
int flags = this.helper.GetFlags(value);
if ((flags & BigNumberFlags.FlagSignalingNaN) != 0) {
return this.SignalingNaNInvalid(value, ctx);
} else if ((flags & BigNumberFlags.FlagQuietNaN) != 0) {
return this.ReturnQuietNaN(
value,
ctx);
}
T ret = ((flags & BigNumberFlags.FlagNegative) != 0) ?
this.helper.CreateNewWithFlags(
this.helper.GetMantissa(value),
this.helper.GetExponent(value),
flags & ~BigNumberFlags.FlagNegative) :
value;
return this.RoundToPrecision(ret, ctx);
}
private T AbsRaw(T value) {
return this.EnsureSign(value, false);
}
// mant1 and mant2 are assumed to be nonnegative
private T AddCore2(
FastIntegerFixed mant1,
FastIntegerFixed mant2,
FastIntegerFixed exponent,
int flags1,
int flags2,
EContext ctx) {
boolean neg1 = (flags1 & BigNumberFlags.FlagNegative) != 0;
boolean neg2 = (flags2 & BigNumberFlags.FlagNegative) != 0;
boolean negResult = false;
// System.out.println("neg1=" + neg1 + " neg2=" + neg2);
if (neg1 != neg2) {
// Signs are different, treat as a subtraction
mant1 = FastIntegerFixed.Subtract(mant1, mant2);
int mant1Sign = mant1.signum();
if (mant1Sign < 0) {
negResult = !neg1;
mant1 = mant1.Negate();
} else if (mant1Sign == 0) {
// Result is negative zero
negResult = neg1 ^ neg2;
if (negResult) {
negResult &= (neg1 && neg2) || ((neg1 ^ neg2) && ctx != null &&
ctx.getRounding() == ERounding.Floor);
}
} else {
negResult = neg1;
}
} else {
// Signs are same, treat as an addition
mant1 = FastIntegerFixed.Add(mant1, mant2);
negResult = neg1;
if (negResult && mant1.isValueZero()) {
// Result is negative zero
negResult &= (neg1 && neg2) || ((neg1 ^ neg2) && ctx != null &&
ctx.getRounding() == ERounding.Floor);
}
}
// System.out.println("mant1= " + mant1 + " exp= " + exponent +" neg= "+
// (negResult));
return this.helper.CreateNewWithFlagsFastInt(
mant1,
exponent,
negResult ? BigNumberFlags.FlagNegative : 0);
}
// mant1 and mant2 are assumed to be nonnegative
private T AddCore(
EInteger mant1,
EInteger mant2,
EInteger exponent,
int flags1,
int flags2,
EContext ctx) {
boolean neg1 = (flags1 & BigNumberFlags.FlagNegative) != 0;
boolean neg2 = (flags2 & BigNumberFlags.FlagNegative) != 0;
boolean negResult = false;
// System.out.println("neg1=" + neg1 + " neg2=" + neg2);
if (neg1 != neg2) {
// Signs are different, treat as a subtraction
// System.out.println("sub " + mant1 + " " + mant2);
mant1 = mant1.Subtract(mant2);
int mant1Sign = mant1.signum();
negResult = neg1 ^ (mant1Sign == 0 ? neg2 : (mant1Sign < 0));
if (mant1Sign < 0) {
mant1 = mant1.Negate();
}
} else {
// Signs are same, treat as an addition
// System.out.println("add " + mant1 + " " + mant2);
mant1 = mant1.Add(mant2);
negResult = neg1;
}
if (negResult && mant1.isZero()) {
// Result is negative zero
negResult &= (neg1 && neg2) || ((neg1 ^ neg2) && ctx != null &&
ctx.getRounding() == ERounding.Floor);
}
// System.out.println("mant1= " + mant1 + " exp= " + exponent +" neg= "+
// (negResult));
return this.helper.CreateNewWithFlags(
mant1,
exponent,
negResult ? BigNumberFlags.FlagNegative : 0);
}
// mant1 and mant2 are assumed to be nonnegative
private T AddCore(
FastIntegerFixed fmant1,
FastIntegerFixed fmant2,
FastIntegerFixed exponent,
int flags1,
int flags2,
EContext ctx) {
boolean neg1 = (flags1 & BigNumberFlags.FlagNegative) != 0;
boolean neg2 = (flags2 & BigNumberFlags.FlagNegative) != 0;
boolean negResult = false;
// System.out.println("neg1=" + neg1 + " neg2=" + neg2);
if (neg1 != neg2) {
// Signs are different, treat as a subtraction
fmant1 = fmant1.Subtract(fmant2);
int mant1Sign = fmant1.signum();
negResult = neg1 ^ (mant1Sign == 0 ? neg2 : (mant1Sign < 0));
if (mant1Sign < 0) {
fmant1 = fmant1.Negate();
}
} else {
// Signs are same, treat as an addition
fmant1 = fmant1.Add(fmant2);
negResult = neg1;
}
if (negResult && fmant1.isValueZero()) {
// Result is negative zero
negResult &= (neg1 && neg2) || ((neg1 ^ neg2) && ctx != null &&
ctx.getRounding() == ERounding.Floor);
}
return this.helper.CreateNewWithFlagsFastInt(
fmant1,
exponent,
negResult ? BigNumberFlags.FlagNegative : 0);
}
private static FastInteger ToFastInteger(FastIntegerFixed fif) {
if (fif.CanFitInInt32()) {
return new FastInteger(fif.ToInt32());
} else {
return FastInteger.FromBig(fif.ToEInteger());
}
}
private T AddExDiffExp(
FastIntegerFixed op1Exponent,
FastIntegerFixed op1Mantissa,
FastIntegerFixed op2Exponent,
FastIntegerFixed op2Mantissa,
int thisFlags,
int otherFlags,
EContext ctx,
int expcmp,
boolean roundToOperandPrecision) {
/*
*/ T retval = null;
// choose the minimum exponent
FastIntegerFixed resultExponent = expcmp < 0 ?
op1Exponent : op2Exponent;
// System.out.println("[" + op1Mantissa + "," + op1Exponent + "], [" +
// op2Mantissa + ", " + op2Exponent + "] -> " + resultExponent);
if (ctx != null && ctx.getHasMaxPrecision() && ctx.getPrecision().signum() > 0) {
FastIntegerFixed expdiff = op1Exponent.Subtract(op2Exponent).Abs();
// Check if exponent difference is too big for
// radix-power calculation to work quickly
boolean op2IsZero = op2Mantissa.isValueZero();
boolean op1IsZero = op1Mantissa.isValueZero();
int thisSign = op1IsZero ? 0 : (((thisFlags &
BigNumberFlags.FlagNegative) != 0) ? -1 : 1);
int otherSign = op2IsZero ? 0 : (((otherFlags &
BigNumberFlags.FlagNegative) != 0) ? -1 : 1);
boolean moreDistantThanPrecision = expdiff.compareTo(ctx.getPrecision()) > 0;
// If exponent difference is greater than the precision
if (moreDistantThanPrecision) {
int expcmp2 = op1Exponent.compareTo(op2Exponent);
if (expcmp2 < 0) {
if (!op2IsZero) {
// first operand's exponent is less
// and second operand isn't zero
// second mantissa will be shifted by the exponent
// difference
// _________________________111111111111|_
// ___222222222222222|____________________
FastIntegerFixed digitLength1 =
NumberUtility.DigitLengthBoundsFixed(this.helper,
op1Mantissa)[1];
// System.out.println("dl1="+digitLength1);
if (op1Exponent.Add(digitLength1).Add(2)
.compareTo(op2Exponent) < 0) {
// first operand's mantissa can't reach the
// second operand's mantissa, so the exponent can be
// raised without affecting the result
FastIntegerFixed tmp = op2Exponent.Subtract(4)
.Subtract(digitLength1).Subtract(ctx.getPrecision());
// System.out.println("tmp="+tmp);
FastIntegerFixed newDiff = tmp.Subtract(op2Exponent).Abs();
// System.out.println("newdiff="+newDiff + " expdiff="+expdiff);
if (newDiff.compareTo(expdiff) < 0) {
// First operand can be treated as almost zero
boolean sameSign = thisSign == otherSign;
FastIntegerFixed digitLength2 =
NumberUtility.DigitLengthFixed(this.helper, op2Mantissa);
// System.out.println("dl2="+digitLength2);
if (digitLength2.compareTo(ctx.getPrecision()) < 0) {
// Second operand's precision too short, extend
// it to the full precision
FastIntegerFixed precisionDiff =
FastIntegerFixed.FromBig(ctx.getPrecision())
.Subtract(digitLength2);
if (!op1IsZero && !sameSign) {
precisionDiff = precisionDiff.Add(2);
}
op2Mantissa = this.TryMultiplyByRadixPowerFastInt(
op2Mantissa,
precisionDiff);
if (op2Mantissa == null) {
return this.SignalInvalidWithMessage(
ctx,
"Result requires too much memory");
}
op2Exponent = op2Exponent.Subtract(precisionDiff);
if (!op1IsZero && !sameSign) {
op2Mantissa = op2Mantissa.Subtract(1);
}
int hoflags = otherFlags;
T other = this.helper.CreateNewWithFlagsFastInt(
op2Mantissa,
op2Exponent,
hoflags);
FastIntegerFixed shift = digitLength2
.Subtract(ctx.getPrecision());
if (op1IsZero && ctx != null && ctx.getHasFlags()) {
ctx.setFlags(ctx.getFlags()|(EContext.FlagRounded));
}
// System.out.println("Second op's prec too short:
// op2Mantissa=" + op2Mantissa + " precdiff= " +
// (precisionDiff));
return this.RoundToPrecisionInternal(
other,
(op1IsZero || sameSign) ? 0 : 1,
(op1IsZero && !sameSign) ? 0 : 1,
ToFastInteger(shift),
false,
ctx);
} else if (!op1IsZero && !sameSign) {
op2Mantissa = this.TryMultiplyByRadixPowerFastInt(
op2Mantissa,
FastIntegerFixed.FromInt32(2));
if (op2Mantissa == null) {
return this.SignalInvalidWithMessage(
ctx,
"Result requires too much memory");
}
op2Exponent = op2Exponent.Subtract(2);
op2Mantissa = op2Mantissa.Subtract(1);
T other = this.helper.CreateNewWithFlagsFastInt(
op2Mantissa,
op2Exponent,
otherFlags);
FastIntegerFixed shift =
digitLength2.Subtract(ctx.getPrecision());
return this.RoundToPrecisionInternal(
other,
0,
0,
ToFastInteger(shift),
false,
ctx);
} else {
FastIntegerFixed shift2 =
digitLength2.Subtract(ctx.getPrecision());
if (!sameSign && ctx != null && ctx.getHasFlags()) {
ctx.setFlags(ctx.getFlags()|(EContext.FlagRounded));
}
T other = this.helper.CreateNewWithFlagsFastInt(
op2Mantissa,
op2Exponent,
otherFlags);
return this.RoundToPrecisionInternal(
other,
0,
sameSign ? 1 : 0,
ToFastInteger(shift2),
false,
ctx);
}
}
}
}
} else if (expcmp2 > 0) {
if (!op1IsZero) {
// first operand's exponent is greater
// and first operand isn't zero
// first mantissa will be shifted by the exponent
// difference
// __111111111111|
// ____________________222222222222222|
FastIntegerFixed digitLength2 =
NumberUtility.DigitLengthBoundsFixed(
this.helper,
op2Mantissa)[1];
if (op2Exponent.Add(digitLength2).Add(2)
.compareTo(op1Exponent) < 0) {
// second operand's mantissa can't reach the
// first operand's mantissa, so the exponent can be
// raised without affecting the result
FastIntegerFixed tmp = op1Exponent.Subtract(4)
.Subtract(digitLength2).Subtract(ctx.getPrecision());
FastIntegerFixed newDiff = tmp.Subtract(op1Exponent).Abs();
if (newDiff.compareTo(expdiff) < 0) {
// Second operand can be treated as almost zero
boolean sameSign = thisSign == otherSign;
digitLength2 = NumberUtility.DigitLengthFixed(this.helper,
op1Mantissa);
if (digitLength2.compareTo(ctx.getPrecision()) < 0) {
// First operand's precision too short; extend it
// to the full precision
FastIntegerFixed precisionDiff =
FastIntegerFixed.FromBig(ctx.getPrecision())
.Subtract(digitLength2);
if (!op2IsZero && !sameSign) {
precisionDiff = precisionDiff.Add(2);
}
op1Mantissa = this.TryMultiplyByRadixPowerFastInt(
op1Mantissa,
precisionDiff);
if (op1Mantissa == null) {
return this.SignalInvalidWithMessage(
ctx,
"Result requires too much memory");
}
op1Exponent = op1Exponent.Subtract(precisionDiff);
if (!op2IsZero && !sameSign) {
op1Mantissa = op1Mantissa.Subtract(1);
}
T thisValue = this.helper.CreateNewWithFlagsFastInt(
op1Mantissa,
op1Exponent,
thisFlags);
FastIntegerFixed shift =
digitLength2.Subtract(ctx.getPrecision());
if (op2IsZero && ctx != null && ctx.getHasFlags()) {
ctx.setFlags(ctx.getFlags()|(EContext.FlagRounded));
}
// System.out.println("thisValue D"+thisValue);
return this.RoundToPrecisionInternal(
thisValue,
(op2IsZero || sameSign) ? 0 : 1,
(op2IsZero && !sameSign) ? 0 : 1,
ToFastInteger(shift),
false,
ctx);
} else if (!op2IsZero && !sameSign) {
op1Mantissa = this.TryMultiplyByRadixPowerFastInt(
op1Mantissa,
FastIntegerFixed.FromInt32(2));
if (op1Mantissa == null) {
return this.SignalInvalidWithMessage(
ctx,
"Result requires too much memory");
}
op1Exponent = op1Exponent.Subtract(2);
op1Mantissa = op1Mantissa.Subtract(1);
T thisValue = this.helper.CreateNewWithFlagsFastInt(
op1Mantissa,
op1Exponent,
thisFlags);
FastIntegerFixed shift =
digitLength2.Subtract(ctx.getPrecision());
return this.RoundToPrecisionInternal(
thisValue,
0,
0,
ToFastInteger(shift),
false,
ctx);
} else {
FastIntegerFixed shift2 =
digitLength2.Subtract(ctx.getPrecision());
if (!sameSign && ctx != null && ctx.getHasFlags()) {
ctx.setFlags(ctx.getFlags()|(EContext.FlagRounded));
}
T thisValue = this.helper.CreateNewWithFlagsFastInt(
op1Mantissa,
op1Exponent,
thisFlags);
return this.RoundToPrecisionInternal(
thisValue,
0,
sameSign ? 1 : 0,
ToFastInteger(shift2),
false,
ctx);
}
}
}
}
}
expcmp = op1Exponent.compareTo(op2Exponent);
resultExponent = expcmp < 0 ? op1Exponent : op2Exponent;
}
}
if (expcmp > 0) {
op1Mantissa = RescaleByExponentDiff(
op1Mantissa,
op1Exponent,
op2Exponent,
this.helper);
if (op1Mantissa == null) {
return this.SignalInvalidWithMessage(
ctx,
"Result requires too much memory");
}
retval = this.AddCore(
op1Mantissa,
op2Mantissa,
resultExponent,
thisFlags,
otherFlags,
ctx);
// System.out.println("expcmp="+expcmp+" retval="+retval);
} else {
// System.out.println("op2m="+op2Mantissa+" exps="+
// op1Exponent+"/"+op2Exponent);
op2Mantissa = RescaleByExponentDiff(
op2Mantissa,
op1Exponent,
op2Exponent,
this.helper);
// System.out.println("op1m="+op1Mantissa+
// " op2m="+op2Mantissa);
if (op2Mantissa == null) {
return this.SignalInvalidWithMessage(
ctx,
"Result requires too much memory");
}
retval = this.AddCore(
op1Mantissa,
op2Mantissa,
resultExponent,
thisFlags,
otherFlags,
ctx);
// System.out.println("expcmp="+expcmp+" retval="+retval);
}
if (roundToOperandPrecision && ctx != null && ctx.getHasMaxPrecision()) {
FastInteger digitLength1 = this.helper.GetDigitLength(
op1Mantissa.ToEInteger());
FastInteger digitLength2 =
this.helper.GetDigitLength(op2Mantissa.ToEInteger());
FastInteger maxDigitLength = (digitLength1.compareTo(digitLength2) >
0) ? digitLength1 :
digitLength2;
maxDigitLength.SubtractBig(ctx.getPrecision());
// System.out.println("retval= " + retval + " maxdl=" +
// maxDigitLength + " prec= " + (ctx.getPrecision()));
return (maxDigitLength.signum() > 0) ? this.RoundToPrecisionInternal(
retval,
0,
0,
maxDigitLength,
false,
ctx) : this.RoundToPrecision(retval, ctx);
// System.out.println("retval now " + retval);
} else {
return IsNullOrSimpleContext(ctx) ? retval :
this.RoundToPrecision(retval, ctx);
}
}
private T CompareToHandleSpecial(
T thisValue,
T other,
boolean treatQuietNansAsSignaling,
EContext ctx) {
int thisFlags = this.helper.GetFlags(thisValue);
int otherFlags = this.helper.GetFlags(other);
if (((thisFlags | otherFlags) & BigNumberFlags.FlagSpecial) != 0) {
// Check this value then the other value for signaling NaN
if ((thisFlags & BigNumberFlags.FlagSignalingNaN) != 0) {
return this.SignalingNaNInvalid(thisValue, ctx);
}
if ((otherFlags & BigNumberFlags.FlagSignalingNaN) != 0) {
return this.SignalingNaNInvalid(other, ctx);
}
if (treatQuietNansAsSignaling) {
if ((thisFlags & BigNumberFlags.FlagQuietNaN) != 0) {
return this.SignalingNaNInvalid(thisValue, ctx);
}
if ((otherFlags & BigNumberFlags.FlagQuietNaN) != 0) {
return this.SignalingNaNInvalid(other, ctx);
}
} else {
// Check this value then the other value for quiet NaN
if ((thisFlags & BigNumberFlags.FlagQuietNaN) != 0) {
return this.ReturnQuietNaN(thisValue, ctx);
}
if ((otherFlags & BigNumberFlags.FlagQuietNaN) != 0) {
return this.ReturnQuietNaN(other, ctx);
}
}
if ((thisFlags & BigNumberFlags.FlagInfinity) != 0) {
// thisValue is infinity
return ((thisFlags & (BigNumberFlags.FlagInfinity |
BigNumberFlags.FlagNegative)) == (otherFlags &
(BigNumberFlags.FlagInfinity |
BigNumberFlags.FlagNegative))) ? this.ValueOf(0, null) :
(((thisFlags & BigNumberFlags.FlagNegative) == 0) ? this.ValueOf(
1,
null) : this.ValueOf(-1, null));
}
if ((otherFlags & BigNumberFlags.FlagInfinity) != 0) {
// the other value is infinity
return ((thisFlags & (BigNumberFlags.FlagInfinity |
BigNumberFlags.FlagNegative)) == (otherFlags &
(BigNumberFlags.FlagInfinity |
BigNumberFlags.FlagNegative))) ? this.ValueOf(0, null) :
(((otherFlags & BigNumberFlags.FlagNegative) == 0) ?
this.ValueOf(-1, null) : this.ValueOf(1, null));
}
}
return null;
}
private static int CompareToHandleSpecial2(
int thisFlags,
int otherFlags) {
// Assumes either value is NaN and/or infinity
{
if ((thisFlags & BigNumberFlags.FlagNaN) != 0) {
if ((otherFlags & BigNumberFlags.FlagNaN) != 0) {
return 0;
}
// Consider NaN to be greater
return 1;
}
if ((otherFlags & BigNumberFlags.FlagNaN) != 0) {
// Consider this to be less than NaN
return -1;
}
if ((thisFlags & BigNumberFlags.FlagInfinity) != 0) {
// thisValue is infinity
return ((thisFlags & (BigNumberFlags.FlagInfinity |
BigNumberFlags.FlagNegative)) == (otherFlags &
(BigNumberFlags.FlagInfinity |
BigNumberFlags.FlagNegative))) ? 0 :
(((thisFlags & BigNumberFlags.FlagNegative) == 0) ? 1 : -1);
}
if ((otherFlags & BigNumberFlags.FlagInfinity) != 0) {
// the other value is infinity
return ((thisFlags & (BigNumberFlags.FlagInfinity |
BigNumberFlags.FlagNegative)) == (otherFlags &
(BigNumberFlags.FlagInfinity |
BigNumberFlags.FlagNegative))) ? 0 :
(((otherFlags & BigNumberFlags.FlagNegative) == 0) ? -1 : 1);
}
}
return 2;
}
private static int CompareToInternal(
TMath thisValue,
TMath otherValue,
boolean reportOOM,
IRadixMathHelper helper) {
int signA = helper.GetSign(thisValue);
int signB = helper.GetSign(otherValue);
if (signA != signB) {
return (signA < signB) ? -1 : 1;
}
if (signB == 0 || signA == 0) {
// Special case: Either operand is zero
return 0;
}
FastIntegerFixed op1Exponent = helper.GetExponentFastInt(thisValue);
FastIntegerFixed op2Exponent = helper.GetExponentFastInt(otherValue);
FastIntegerFixed op1Mantissa = helper.GetMantissaFastInt(thisValue);
FastIntegerFixed op2Mantissa = helper.GetMantissaFastInt(otherValue);
int expcmp = op1Exponent.compareTo(op2Exponent);
// At this point, the signs are equal so we can compare
// their absolute values instead
int mantcmp = op1Mantissa.compareTo(op2Mantissa);
if (mantcmp == 0) {
// Special case: Mantissas are equal
return signA < 0 ? -expcmp : expcmp;
}
if (expcmp == 0) {
return signA < 0 ? -mantcmp : mantcmp;
}
if (op1Exponent.CanFitInInt32() && op2Exponent.CanFitInInt32()) {
if (op1Mantissa.CanFitInInt32() && op2Mantissa.CanFitInInt32()) {
int e1int = op1Exponent.ToInt32();
int e2int = op2Exponent.ToInt32();
int c = CompareToFast(
e1int,
e2int,
expcmp,
signA,
op1Mantissa,
op2Mantissa,
helper.GetRadix());
if (c <= 1) {
return c;
}
} else if (op1Mantissa.CanFitInInt64() && op2Mantissa.CanFitInInt64()) {
int e1int = op1Exponent.ToInt32();
int e2int = op2Exponent.ToInt32();
int c = CompareToFast64(
e1int,
e2int,
expcmp,
signA,
op1Mantissa,
op2Mantissa,
helper.GetRadix());
if (c <= 1) {
return c;
}
}
}
return CompareToSlow(
op1Exponent.ToEInteger(),
op2Exponent.ToEInteger(),
expcmp,
signA,
op1Mantissa.ToEInteger(),
op2Mantissa.ToEInteger(),
helper,
reportOOM);
}
private T DivideInternal(
T thisValue,
T divisor,
EContext ctx,
int integerMode,
EInteger desiredExponent) {
T ret = this.DivisionHandleSpecial(thisValue, divisor, ctx);
if ((Object)ret != (Object)null) {
return ret;
}
int signA = this.helper.GetSign(thisValue);
int signB = this.helper.GetSign(divisor);
if (signB == 0) {
if (signA == 0) {
return this.SignalInvalid(ctx);
}
boolean flagsNeg = ((this.helper.GetFlags(thisValue) &
BigNumberFlags.FlagNegative) != 0) ^
((this.helper.GetFlags(divisor) &
BigNumberFlags.FlagNegative) != 0);
return this.SignalDivideByZero(ctx, flagsNeg);
}
int radix = this.thisRadix;
if (signA == 0) {
T retval = null;
if (integerMode == IntegerModeFixedScale) {
int newflags = (this.helper.GetFlags(thisValue) &
BigNumberFlags.FlagNegative) ^ (this.helper.GetFlags(divisor) &
BigNumberFlags.FlagNegative);
retval = this.helper.CreateNewWithFlags(
EInteger.FromInt32(0),
desiredExponent,
newflags);
} else {
EInteger dividendExp = this.helper.GetExponent(thisValue);
EInteger divisorExp = this.helper.GetExponent(divisor);
int newflags = (this.helper.GetFlags(thisValue) &
BigNumberFlags.FlagNegative) ^ (this.helper.GetFlags(divisor) &
BigNumberFlags.FlagNegative);
retval = this.helper.CreateNewWithFlags(
EInteger.FromInt32(0),
dividendExp.Subtract(divisorExp),
newflags);
retval = this.RoundToPrecision(retval, ctx);
}
return retval;
} else {
EInteger mantissaDividend = this.helper.GetMantissa(thisValue);
EInteger mantissaDivisor = this.helper.GetMantissa(divisor);
FastIntegerFixed expDividend =
this.helper.GetExponentFastInt(thisValue);
FastIntegerFixed expDivisor = this.helper.GetExponentFastInt(divisor);
boolean resultNeg = (this.helper.GetFlags(thisValue) &
BigNumberFlags.FlagNegative) != (this.helper.GetFlags(divisor) &
BigNumberFlags.FlagNegative);
FastInteger expdiff = FastIntegerFixed.Subtract(expDividend,
expDivisor).ToFastInteger();
EInteger eintPrecision = (ctx == null || !ctx.getHasMaxPrecision()) ?
EInteger.FromInt32(0) : ctx.getPrecision();
if (integerMode == IntegerModeFixedScale) {
FastInteger shift;
EInteger rem;
FastInteger fastDesiredExponent =
FastInteger.FromBig(desiredExponent);
if (ctx != null && ctx.getHasFlags() &&
fastDesiredExponent.compareTo(expdiff) > 0) {
// Treat as rounded if the desired exponent is greater
// than the "ideal" exponent
ctx.setFlags(ctx.getFlags()|(EContext.FlagRounded));
}
if (expdiff.compareTo(fastDesiredExponent) <= 0) {
shift = fastDesiredExponent.Copy().Subtract(expdiff);
EInteger quo;
{
EInteger[] divrem = mantissaDividend.DivRem(mantissaDivisor);
quo = divrem[0];
rem = divrem[1];
}
return this.RoundToScale(
quo,
rem,
mantissaDivisor,
desiredExponent,
shift,
resultNeg,
ctx);
}
if (ctx != null && ctx.getPrecision().signum() != 0 &&
expdiff.Copy().SubtractInt(8).compareTo(eintPrecision) >
0) {
// NOTE: 8 guard digits
// Result would require a too-high precision since
// exponent difference is much higher
return this.SignalInvalidWithMessage(
ctx,
"Result can't fit the precision");
} else {
shift = expdiff.Copy().Subtract(fastDesiredExponent);
mantissaDividend =
this.TryMultiplyByRadixPower(mantissaDividend, shift);
if (mantissaDividend == null) {
return this.SignalInvalidWithMessage(
ctx,
"Result requires too much memory");
}
EInteger quo;
{
EInteger[] divrem = mantissaDividend.DivRem(mantissaDivisor);
quo = divrem[0];
rem = divrem[1];
}
return this.RoundToScale(
quo,
rem,
mantissaDivisor,
desiredExponent,
new FastInteger(0),
resultNeg,
ctx);
}
}
if (integerMode == IntegerModeRegular) {
EInteger rem = null;
EInteger quo = null;
FastInteger natexp = null;
boolean binaryOpt = this.thisRadix == 2 &&
expDivisor.CompareToInt(0) == 0 && expDividend.CompareToInt(0) ==
0 &&
ctx != null && ctx.getHasMaxPrecision() && ctx.getPrecision().compareTo(53)
<= 0 &&
mantissaDividend.CanFitInInt64() && mantissaDivisor.CanFitInInt64();
if (binaryOpt) {
EInteger absdivd = mantissaDividend.Abs();
EInteger absdivs = mantissaDivisor.Abs();
int maxprec = ctx.getPrecision().ToInt32Checked();
int divdCount = (int)absdivd.GetUnsignedBitLengthAsInt64();
int divsCount = (int)mantissaDivisor.GetUnsignedBitLengthAsInt64();
int dividendShift = (divdCount <= divsCount) ? ((divsCount -
divdCount) + maxprec + 1) : Math.max(0,
(maxprec + 1) - (divdCount - divsCount));
absdivd = absdivd.ShiftLeft(dividendShift);
EInteger[] divrem3 = absdivd.DivRem(absdivs);
quo = divrem3[0];
rem = divrem3[1];
if (ctx == EContext.Binary64 && quo.CanFitInInt64() &&
rem.CanFitInInt64()) {
long lquo = quo.ToInt64Checked();
long lrem = rem.ToInt64Checked();
int nexp = -dividendShift;
if (lquo >= (1L << 53)) {
while (lquo >= (1L << 54)) {
lrem |= lquo & 1L;
lquo >>= 1;
++nexp;
}
if ((lquo & 3L) == 3 && lrem == 0) {
lquo >>= 1;
++lquo;
++nexp;
} else if ((lquo & 1L) != 0 && lrem != 0) {
lquo >>= 1;
++lquo;
++nexp;
} else {
lquo >>= 1;
++nexp;
}
while (lquo >= (1L << 53)) {
lquo >>= 1;
++nexp;
}
return this.helper.CreateNewWithFlags(
EInteger.FromInt64(lquo),
EInteger.FromInt64(nexp),
resultNeg ? BigNumberFlags.FlagNegative : 0);
}
}
if (ctx == EContext.Binary32 && quo.CanFitInInt64() &&
rem.CanFitInInt64()) {
long lquo = quo.ToInt64Checked();
long lrem = rem.ToInt64Checked();
int nexp = -dividendShift;
if (lquo >= (1L << 24)) {
while (lquo >= (1L << 25)) {
lrem |= lquo & 1L;
lquo >>= 1;
++nexp;
}
if ((lquo & 3L) == 3 && lrem == 0) {
lquo >>= 1;
++lquo;
++nexp;
} else if ((lquo & 1L) != 0 && lrem != 0) {
lquo >>= 1;
++lquo;
++nexp;
} else {
lquo >>= 1;
++nexp;
}
while (lquo >= (1L << 24)) {
lquo >>= 1;
++nexp;
}
return this.helper.CreateNewWithFlags(
EInteger.FromInt64(lquo),
EInteger.FromInt64(nexp),
resultNeg ? BigNumberFlags.FlagNegative : 0);
}
}
natexp = new FastInteger(-dividendShift);
// System.out.println(quo.GetUnsignedBitLengthAsInt64()+" "+
// rem.GetUnsignedBitLengthAsInt64()+" "+
// (ctx == EContext.Binary64));
}
if (!binaryOpt) {
EInteger[] divrem = mantissaDividend.DivRem(mantissaDivisor);
quo = divrem[0];
rem = divrem[1];
if (rem.isZero()) {
// Dividend is divisible by divisor
// System.out.println("divisible dividend: quo length=" +
// quo.GetUnsignedBitLengthAsInt64());
quo = quo.Abs();
T fi = this.helper.CreateNewWithFlagsFastInt(
FastIntegerFixed.FromBig(quo),
FastIntegerFixed.FromFastInteger(expdiff),
resultNeg ? BigNumberFlags.FlagNegative : 0);
return this.RoundToPrecision(fi, ctx);
}
}
if (ctx != null && ctx.getHasMaxPrecision()) {
if (!binaryOpt) {
EInteger divid = mantissaDividend;
FastInteger shift = FastInteger.FromBig(ctx.getPrecision());
FastInteger[] dividBounds =
NumberUtility.DigitLengthBounds(this.helper, mantissaDividend);
FastInteger[] divisBounds =
NumberUtility.DigitLengthBounds(this.helper, mantissaDivisor);
if (dividBounds[0].Copy().Subtract(divisBounds[1])
.compareTo(shift) > 0) {
// Dividend is already bigger than divisor by at least
// shift digits, so no need to shift
shift.SetInt(0);
} else {
FastInteger shiftCalc = divisBounds[0].Copy().Subtract(
dividBounds[1]).AddInt(2).Add(shift);
if (shiftCalc.CompareToInt(0) <= 0) {
// No need to shift
shift.SetInt(0);
} else {
shift = shiftCalc;
divid = this.TryMultiplyByRadixPower(divid, shift);
if (divid == null) {
return this.SignalInvalidWithMessage(
ctx,
"Result requires too much memory");
}
}
}
if (shift.signum() != 0 || quo == null) {
// if shift isn't zero, recalculate the quotient
// and remainder
EInteger[] divrem2 = divid.DivRem(mantissaDivisor);
quo = divrem2[0];
rem = divrem2[1];
}
natexp = expdiff.Copy().Subtract(shift);
}
// System.out.println(String.Format("" + divid + "" +
// mantissaDivisor + " -> quo= " + quo + " rem= " +
// (rem)));
int[] digitStatus = this.RoundToScaleStatus(
rem,
mantissaDivisor,
ctx);
if (digitStatus == null) {
// NOTE: Can only happen for ERounding.None
return this.SignalInvalidWithMessage(
ctx,
"Rounding was required");
}
T retval2 = this.helper.CreateNewWithFlags(
quo,
natexp.ToEInteger(),
resultNeg ? BigNumberFlags.FlagNegative : 0);
if ((ctx == null || !ctx.getHasFlagsOrTraps()) &&
(digitStatus[0] | digitStatus[1]) != 0) {
// Context doesn't care about flags, and
// we already know the result is inexact, so no
// need to create a blank flag context to find that out
return this.RoundToPrecisionInternal(
retval2,
digitStatus[0],
digitStatus[1],
null,
false,
ctx);
}
EContext ctxcopy = ctx.WithBlankFlags();
retval2 = this.RoundToPrecisionInternal(
retval2,
digitStatus[0],
digitStatus[1],
null,
false,
ctxcopy);
if ((ctxcopy.getFlags() & EContext.FlagInexact) != 0) {
if (ctx.getHasFlags()) {
ctx.setFlags(ctx.getFlags()|(ctxcopy.getFlags()));
}
return retval2;
}
if (ctx.getHasFlags()) {
ctx.setFlags(ctx.getFlags()|(ctxcopy.getFlags() & ~EContext.FlagRounded));
}
retval2 = this.ReduceToPrecisionAndIdealExponent(
retval2,
ctx,
rem.isZero() ? null : FastInteger.FromBig(eintPrecision),
expdiff);
return retval2;
}
}
// Rest of method assumes unlimited precision
// and IntegerModeRegular
FastInteger adjust = new FastInteger(0);
FastInteger result = new FastInteger(0);
int mantcmp = mantissaDividend.compareTo(mantissaDivisor);
if (mantcmp == 0) {
result = new FastInteger(1);
mantissaDividend = EInteger.FromInt32(0);
} else {
EInteger gcd = mantissaDividend.Gcd(mantissaDivisor);
// System.out.println("mgcd/den1=" + mantissaDividend + "/" + (//
// mantissaDivisor) + "/" + gcd);
if (gcd.compareTo(EInteger.FromInt32(1)) != 0) {
mantissaDividend = mantissaDividend.Divide(gcd);
mantissaDivisor = mantissaDivisor.Divide(gcd);
}
// System.out.println("mgcd/den2=" + mantissaDividend + "/" + (//
// mantissaDivisor) + "/" + gcd);
FastInteger divShift = this.helper.DivisionShift(
mantissaDividend,
mantissaDivisor);
if (divShift == null) {
return this.SignalInvalidWithMessage(
ctx,
"Result would have a nonterminating expansion");
}
mantissaDividend = this.helper.MultiplyByRadixPower(
mantissaDividend,
divShift);
adjust = divShift.Copy();
// System.out.println("mant " + mantissaDividend + " " +
// (// mantissaDivisor));
EInteger[] quorem = mantissaDividend.DivRem(mantissaDivisor);
mantissaDividend = quorem[1];
result = FastInteger.FromBig(quorem[0]);
}
// mantissaDividend now has the remainder
FastInteger exp = expdiff.Copy().Subtract(adjust);
ERounding rounding = (ctx == null) ? ERounding.HalfEven : ctx.getRounding();
int lastDiscarded = 0;
int olderDiscarded = 0;
if (!mantissaDividend.isZero()) {
if (rounding == ERounding.HalfDown || rounding ==
ERounding.HalfEven || rounding == ERounding.HalfUp) {
int cmpHalf = CompareToHalf(mantissaDividend, mantissaDivisor);
if (cmpHalf == 0) {
// remainder is exactly half
lastDiscarded = radix / 2;
olderDiscarded = 0;
} else if (cmpHalf > 0) {
// remainder is greater than half
lastDiscarded = radix / 2;
olderDiscarded = 1;
} else {
// remainder is less than half
lastDiscarded = 0;
olderDiscarded = 1;
}
} else {
if (rounding == ERounding.None) {
return this.SignalInvalidWithMessage(
ctx,
"Rounding was required");
}
lastDiscarded = 1;
olderDiscarded = 1;
}
}
EInteger bigResult = result.ToEInteger();
if (ctx != null && ctx.getHasFlags() && exp.compareTo(expdiff) > 0) {
// Treat as rounded if the true exponent is greater
// than the "ideal" exponent
ctx.setFlags(ctx.getFlags()|(EContext.FlagRounded));
}
EInteger bigexp = exp.ToEInteger();
T retval = this.helper.CreateNewWithFlags(
bigResult,
bigexp,
resultNeg ? BigNumberFlags.FlagNegative : 0);
return this.RoundToPrecisionInternal(
retval,
lastDiscarded,
olderDiscarded,
null,
false,
ctx);
}
}
private T DivisionHandleSpecial(
T thisValue,
T other,
EContext ctx) {
int thisFlags = this.helper.GetFlags(thisValue);
int otherFlags = this.helper.GetFlags(other);
if (((thisFlags | otherFlags) & BigNumberFlags.FlagSpecial) != 0) {
T result = this.HandleNotANumber(thisValue, other, ctx);
if ((Object)result != (Object)null) {
return result;
}
if ((thisFlags & BigNumberFlags.FlagInfinity) != 0 && (otherFlags &
BigNumberFlags.FlagInfinity) != 0) {
// Attempt to divide infinity by infinity
return this.SignalInvalid(ctx);
}
if ((thisFlags & BigNumberFlags.FlagInfinity) != 0) {
return this.EnsureSign(
thisValue,
((thisFlags ^ otherFlags) & BigNumberFlags.FlagNegative) != 0);
}
if ((otherFlags & BigNumberFlags.FlagInfinity) != 0) {
// Divisor is infinity, so result will be epsilon
if (ctx != null && ctx.getHasExponentRange() && ctx.getPrecision().signum() > 0) {
if (ctx.getHasFlags()) {
ctx.setFlags(ctx.getFlags()|(EContext.FlagClamped));
}
EInteger bigexp = ctx.getEMin();
EInteger bigprec = ctx.getPrecision();
if (ctx.getAdjustExponent()) {
bigexp = bigexp.Subtract(bigprec);
bigexp = bigexp.Add(EInteger.FromInt32(1));
}
thisFlags = (thisFlags ^ otherFlags) & BigNumberFlags.FlagNegative;
return this.helper.CreateNewWithFlags(
EInteger.FromInt32(0),
bigexp,
thisFlags);
}
thisFlags = (thisFlags ^ otherFlags) & BigNumberFlags.FlagNegative;
result = this.helper.CreateNewWithFlags(
EInteger.FromInt32(0),
EInteger.FromInt32(0),
thisFlags);
return this.RoundToPrecision(
result,
ctx);
}
}
return null;
}
private T EnsureSign(T val, boolean negative) {
if (val == null) {
return val;
}
int flags = this.helper.GetFlags(val);
if ((negative && (flags & BigNumberFlags.FlagNegative) == 0) ||
(!negative && (flags & BigNumberFlags.FlagNegative) != 0)) {
flags &= ~BigNumberFlags.FlagNegative;
flags |= negative ? BigNumberFlags.FlagNegative : 0;
return this.helper.CreateNewWithFlags(
this.helper.GetMantissa(val),
this.helper.GetExponent(val),
flags);
}
return val;
}
private T ExpInternalVeryCloseToZero(
T thisValue,
EInteger workingPrecision,
EContext ctx) {
// NOTE: Assumes 'thisValue' is very close to zero
// and either positive or negative.
// System.out.println("ExpInternalVeryCloseToZero");
T zero = this.helper.ValueOf(0);
int cmpZero = this.compareTo(thisValue, zero);
if (cmpZero == 0) {
// NOTE: Should not happen here, because
// the check for zero should have happened earlier
throw new IllegalStateException();
}
T one = this.helper.ValueOf(1);
int precisionAdd = this.thisRadix == 2 ? 18 : 12;
EContext ctxdiv = SetPrecisionIfLimited(
ctx,
workingPrecision.Add(EInteger.FromInt32(precisionAdd)))
.WithRounding(ERounding.HalfEven);
EInteger bigintN = EInteger.FromInt64(2);
EInteger facto = EInteger.FromInt32(1);
T guess;
// Guess starts with thisValue
guess = thisValue;
// System.out.println("startguess="+guess);
T lastGuess = guess;
T pow = thisValue;
boolean more = true;
int lastCompare = 0;
int vacillations = 0;
int maxvac = cmpZero < 0 ? 10 : 3;
while (true) {
lastGuess = guess;
// Iterate by:
// newGuess = guess + (thisValue^n/factorial(n))
// (n starts at 2 and increases by 1 after
// each iteration)
pow = this.Multiply(pow, thisValue, ctxdiv);
facto = facto.Multiply(bigintN);
T tmp = this.Divide(
pow,
this.helper.CreateNewWithFlags(facto, EInteger.FromInt32(0), 0),
ctxdiv);
T newGuess = this.Add(guess, tmp, ctxdiv);
// System.out.println("newguess " + newGuess);
// System.out.println("newguessN " + NextPlus(newGuess,ctxdiv));
{
int guessCmp = this.compareTo(lastGuess, newGuess);
// System.out.println("guessCmp = " + guessCmp + ", vac=" + vacillations);
if (guessCmp == 0) {
more = false;
} else if ((guessCmp > 0 && lastCompare < 0) || (lastCompare > 0 &&
guessCmp < 0)) {
// Guesses are vacillating
++vacillations;
more &= vacillations <= maxvac ||
(cmpZero < 0 ? guessCmp >= 0 : guessCmp <= 0);
}
lastCompare = guessCmp;
}
if (more) {
bigintN = bigintN.Add(EInteger.FromInt32(1));
guess = newGuess;
} else {
T ret = newGuess;
// Add 1 at end
ret = this.Add(one, ret, ctx);
return ret;
}
}
}
private T ExpInternal(
T thisValue,
EInteger workingPrecision,
EContext ctx) {
// System.out.println("ExpInternal " +(thisValue as
// EDecimal)?.ToDouble()+", wp=" +workingPrecision);
T zero = this.helper.ValueOf(0);
if (this.compareTo(thisValue, zero) == 0) {
// NOTE: Should not happen here, because
// the check for zero should have happened earlier
throw new IllegalStateException();
}
T one = this.helper.ValueOf(1);
int precisionAdd = this.thisRadix == 2 ? 18 : 12;
EContext ctxdiv = SetPrecisionIfLimited(
ctx,
workingPrecision.Add(EInteger.FromInt32(precisionAdd)))
.WithRounding(ERounding.HalfEven);
EInteger bigintN = EInteger.FromInt64(2);
EInteger facto = EInteger.FromInt32(1);
T guess;
// Guess starts with thisValue
// guess = thisValue;
// Guess starts with 1 + thisValue
guess = this.Add(one, thisValue, ctxdiv);
// System.out.println(ctxdiv.toString());
// System.out.println("tv="+(((thisValue instanceof EDecimal) ? (EDecimal)thisValue : null))?.ToDouble());
// System.out.println("initial="+thisValue);
// System.out.println("startguess="+guess);
T lastGuess = guess;
T pow = thisValue;
boolean more = true;
int lastCompare = 0;
int vacillations = 0;
while (true) {
lastGuess = guess;
// Iterate by:
// newGuess = guess + (thisValue^n/factorial(n))
// (n starts at 2 and increases by 1 after
// each iteration)
pow = this.Multiply(pow, thisValue, ctxdiv);
facto = facto.Multiply(bigintN);
T tmp = this.Divide(
pow,
this.helper.CreateNewWithFlags(facto, EInteger.FromInt32(0), 0),
ctxdiv);
T newGuess = this.Add(guess, tmp, ctxdiv);
// System.out.println("newguess " +
// this.helper.GetMantissa(newGuess));
// System.out.println("newguessN " + NextPlus(newGuess,ctxdiv));
{
int guessCmp = this.compareTo(lastGuess, newGuess);
// System.out.println("guessCmp = " + guessCmp);
if (guessCmp == 0) {
more = false;
} else if ((guessCmp > 0 && lastCompare < 0) || (lastCompare > 0 &&
guessCmp < 0)) {
// Guesses are vacillating
++vacillations;
more &= vacillations <= 3 || guessCmp <= 0;
}
lastCompare = guessCmp;
}
if (more) {
bigintN = bigintN.Add(EInteger.FromInt32(1));
guess = newGuess;
} else {
T ret = this.Add(guess, tmp, ctx);
// System.out.println("final... " + ret);
return ret;
}
}
}
private T ExtendPrecision(T thisValue, EContext ctx) {
if (ctx == null || !ctx.getHasMaxPrecision()) {
return this.RoundToPrecision(thisValue, ctx);
}
EInteger mant = this.helper.GetMantissa(thisValue);
FastInteger digits = this.helper.GetDigitLength(mant);
FastInteger fastPrecision = FastInteger.FromBig(ctx.getPrecision());
EInteger exponent = this.helper.GetExponent(thisValue);
if (digits.compareTo(fastPrecision) < 0) {
fastPrecision.Subtract(digits);
mant = this.TryMultiplyByRadixPower(mant, fastPrecision);
if (mant == null) {
return this.SignalInvalidWithMessage(
ctx,
"Result requires too much memory");
}
EInteger bigPrec = fastPrecision.ToEInteger();
exponent = exponent.Subtract(bigPrec);
}
if (ctx != null && ctx.getHasFlags()) {
ctx.setFlags(ctx.getFlags()|(EContext.FlagRounded));
ctx.setFlags(ctx.getFlags()|(EContext.FlagInexact));
}
return this.RoundToPrecision(
this.helper.CreateNewWithFlags(mant, exponent, 0),
ctx);
}
private T HandleNotANumber(T thisValue, T other, EContext ctx) {
int thisFlags = this.helper.GetFlags(thisValue);
int otherFlags = this.helper.GetFlags(other);
// Check this value then the other value for signaling NaN
if ((thisFlags & BigNumberFlags.FlagSignalingNaN) != 0) {
return this.SignalingNaNInvalid(thisValue, ctx);
}
if ((otherFlags & BigNumberFlags.FlagSignalingNaN) != 0) {
return this.SignalingNaNInvalid(other, ctx);
}
// Check this value then the other value for quiet NaN
return ((thisFlags & BigNumberFlags.FlagQuietNaN) != 0) ?
this.ReturnQuietNaN(thisValue, ctx) : (((otherFlags &
BigNumberFlags.FlagQuietNaN) != 0) ? this.ReturnQuietNaN(
other,
ctx) : null);
}
private boolean IsFinite(T val) {
return (this.helper.GetFlags(val) & BigNumberFlags.FlagSpecial) == 0;
}
private boolean IsNegative(T val) {
return (this.helper.GetFlags(val) & BigNumberFlags.FlagNegative) != 0;
}
private boolean IsWithinExponentRangeForPow(
T thisValue,
EContext ctx) {
if (ctx == null || !ctx.getHasExponentRange()) {
return true;
}
FastInteger digits = this.helper.GetDigitLength(
this.helper.GetMantissa(thisValue));
EInteger exp = this.helper.GetExponent(thisValue);
FastInteger fi = FastInteger.FromBig(exp);
if (ctx.getAdjustExponent()) {
fi.Add(digits);
fi.Decrement();
}
// System.out.println("" + exp + " -> " + fi);
if (fi.signum() < 0) {
fi.Negate().Divide(2).Negate();
// System.out.println("" + exp + " II -> " + fi);
}
exp = fi.ToEInteger();
return exp.compareTo(ctx.getEMin()) >= 0 && exp.compareTo(ctx.getEMax()) <= 0;
}
private T LnInternalCloseToOne2(
T thisValue,
EInteger workingPrecision,
EContext ctx) {
// Assumes 'thisValue' is close to 1
boolean more = true;
int lastCompare = 0;
int vacillations = 0;
String dbg = "";
// if (thisValue instanceof EDecimal) {
// dbg="" + ((EDecimal)thisValue).ToDouble();
// }
// System.out.println("workingprec=" + workingPrecision);
EContext ctxdiv = SetPrecisionIfLimited(
ctx,
workingPrecision.Add(EInteger.FromInt64(6))).WithRounding(ERounding.HalfEven);
T rzlo = this.Add(thisValue, this.helper.ValueOf(-1), null);
T rzhi = this.Add(thisValue, this.helper.ValueOf(1), null);
// (thisValue - 1) / (thisValue + 1)
T rz = this.Divide(rzlo, rzhi, ctxdiv);
// (thisValue - 1) * 2 / (thisValue + 1)
T guess = this.Add(rz, rz, null);
T rzterm = rz;
T lastGuess = null;
T lastDiff = null;
boolean haveLastDiff = false;
EInteger denom = EInteger.FromInt32(3);
EInteger iterations = EInteger.FromInt32(0);
while (more) {
lastGuess = guess;
rzterm = this.Multiply(rzterm, rz, ctxdiv);
rzterm = this.Multiply(rzterm, rz, ctxdiv);
T rd = this.Divide(
this.Multiply(rzterm, this.helper.ValueOf(2), ctxdiv),
this.helper.CreateNewWithFlags(denom, EInteger.FromInt32(0), 0),
ctxdiv);
if (haveLastDiff && this.compareTo(lastDiff, rd) == 0) {
// iterate is the same as before, so break
break;
}
T newGuess = this.Add(guess, rd, ctxdiv);
int guessCmp = this.compareTo(lastGuess, newGuess);
boolean overprec = iterations.compareTo(workingPrecision) >= 0;
if (guessCmp == 0) {
more = false;
} else if ((guessCmp > 0 && lastCompare < 0) || (lastCompare > 0 &&
guessCmp < 0)) {
// Guesses are vacillating
++vacillations;
more &= !overprec || vacillations <= 3 || guessCmp <= 0;
}
lastCompare = guessCmp;
lastDiff = this.AbsRaw(rd);
haveLastDiff = true;
if (more) {
denom = denom.Add(2);
iterations = iterations.Add(EInteger.FromInt32(1));
guess = newGuess;
} else {
guess = this.Add(guess, rd, ctx);
}
}
// System.out.println("iterations="+iterations+", "+dbg);
return guess;
}
private T LnInternal(
T thisValue,
EInteger workingPrecision,
EContext ctx) {
boolean more = true;
int lastCompare = 0;
int vacillations = 0;
EContext ctxdiv = SetPrecisionIfLimited(
ctx,
workingPrecision.Add(EInteger.FromInt64(6)))
.WithRounding(ERounding.HalfEven);
T z = this.Add(
this.NegateRaw(thisValue),
this.helper.ValueOf(1),
null);
T zpow = this.Multiply(z, z, ctxdiv);
T guess = this.NegateRaw(z);
T lastGuess = null;
EInteger denom = EInteger.FromInt64(2);
while (more) {
lastGuess = guess;
T tmp = this.Divide(
zpow,
this.helper.CreateNewWithFlags(denom, EInteger.FromInt32(0), 0),
ctxdiv);
T newGuess = this.Add(guess, this.NegateRaw(tmp), ctxdiv);
{
int guessCmp = this.compareTo(lastGuess, newGuess);
if (guessCmp == 0) {
more = false;
} else if ((guessCmp > 0 && lastCompare < 0) || (lastCompare > 0 &&
guessCmp < 0)) {
// Guesses are vacillating
++vacillations;
more &= vacillations <= 3 || guessCmp <= 0;
}
lastCompare = guessCmp;
}
guess = newGuess;
if (more) {
zpow = this.Multiply(zpow, z, ctxdiv);
denom = denom.Add(EInteger.FromInt32(1));
}
}
return this.RoundToPrecision(guess, ctx);
}
private T MinMaxHandleSpecial(
T thisValue,
T otherValue,
EContext ctx,
boolean isMinOp,
boolean compareAbs) {
int thisFlags = this.helper.GetFlags(thisValue);
int otherFlags = this.helper.GetFlags(otherValue);
if (((thisFlags | otherFlags) & BigNumberFlags.FlagSpecial) != 0) {
// Check this value then the other value for signaling NaN
if ((thisFlags & BigNumberFlags.FlagSignalingNaN) != 0) {
return this.SignalingNaNInvalid(thisValue, ctx);
}
if ((otherFlags & BigNumberFlags.FlagSignalingNaN) != 0) {
return this.SignalingNaNInvalid(otherValue, ctx);
}
// Check this value then the other value for quiet NaN
if ((thisFlags & BigNumberFlags.FlagQuietNaN) != 0) {
if ((otherFlags & BigNumberFlags.FlagQuietNaN) != 0) {
// both values are quiet NaN
return this.ReturnQuietNaN(thisValue, ctx);
}
// return "other" for being numeric
return this.RoundToPrecision(otherValue, ctx);
}
if ((otherFlags & BigNumberFlags.FlagQuietNaN) != 0) {
// At this point, "thisValue" can't be NaN,
// return "thisValue" for being numeric
return this.RoundToPrecision(thisValue, ctx);
}
if ((thisFlags & BigNumberFlags.FlagInfinity) != 0) {
if (compareAbs && (otherFlags & BigNumberFlags.FlagInfinity) == 0) {
// treat this as larger
return isMinOp ? this.RoundToPrecision(otherValue, ctx) :
thisValue;
}
// This value is infinity
if (isMinOp) {
// if negative, will be less than every other number
return ((thisFlags & BigNumberFlags.FlagNegative) != 0) ?
thisValue : this.RoundToPrecision(otherValue, ctx);
// if positive, will be greater
}
// if positive, will be greater than every other number
return ((thisFlags & BigNumberFlags.FlagNegative) == 0) ?
thisValue : this.RoundToPrecision(otherValue, ctx);
}
if ((otherFlags & BigNumberFlags.FlagInfinity) != 0) {
if (compareAbs) {
// treat this as larger (the first value
// won't be infinity at this point
return isMinOp ? this.RoundToPrecision(thisValue, ctx) :
otherValue;
}
return isMinOp ? (((otherFlags & BigNumberFlags.FlagNegative) ==
0) ? this.RoundToPrecision(thisValue, ctx) :
otherValue) : (((otherFlags & BigNumberFlags.FlagNegative) !=
0) ? this.RoundToPrecision(thisValue, ctx) : otherValue);
}
}
return null;
}
private T MultiplyAddHandleSpecial(
T op1,
T op2,
T op3,
EContext ctx) {
int op1Flags = this.helper.GetFlags(op1);
// Check operands in order for signaling NaN
if ((op1Flags & BigNumberFlags.FlagSignalingNaN) != 0) {
return this.SignalingNaNInvalid(op1, ctx);
}
int op2Flags = this.helper.GetFlags(op2);
if ((op2Flags & BigNumberFlags.FlagSignalingNaN) != 0) {
return this.SignalingNaNInvalid(op2, ctx);
}
int op3Flags = this.helper.GetFlags(op3);
// Check operands in order for quiet NaN
if ((op1Flags & BigNumberFlags.FlagQuietNaN) != 0) {
if ((op3Flags & BigNumberFlags.FlagSignalingNaN) != 0) {
return this.SignalingNaNInvalid(op3, ctx);
} else {
return this.ReturnQuietNaN(op1, ctx);
}
}
if ((op2Flags & BigNumberFlags.FlagQuietNaN) != 0) {
if ((op3Flags & BigNumberFlags.FlagSignalingNaN) != 0) {
return this.SignalingNaNInvalid(op3, ctx);
} else {
return this.ReturnQuietNaN(op2, ctx);
}
}
// Check multiplying infinity by 0 (important to check
// now before checking third operand for quiet NaN because
// this signals invalid operation and the operation starts
// with multiplying only the first two operands)
if ((op1Flags & BigNumberFlags.FlagInfinity) != 0) {
// Attempt to multiply infinity by 0
if ((op2Flags & BigNumberFlags.FlagSpecial) == 0 &&
this.helper.GetMantissa(op2).isZero()) {
return this.SignalInvalid(ctx);
}
}
if ((op2Flags & BigNumberFlags.FlagInfinity) != 0) {
// Attempt to multiply infinity by 0
if ((op1Flags & BigNumberFlags.FlagSpecial) == 0 &&
this.helper.GetMantissa(op1).isZero()) {
return this.SignalInvalid(ctx);
}
}
// Now check third operand for signaling NaN
if ((op3Flags & BigNumberFlags.FlagSignalingNaN) != 0) {
return this.SignalingNaNInvalid(op3, ctx);
}
// Now check third operand for quiet NaN
return ((op3Flags & BigNumberFlags.FlagQuietNaN) != 0) ?
this.ReturnQuietNaN(op3, ctx) : null;
}
private T NegateRaw(T val) {
if (val == null) {
return val;
}
int sign = this.helper.GetFlags(val) & BigNumberFlags.FlagNegative;
return this.helper.CreateNewWithFlags(
this.helper.GetMantissa(val),
this.helper.GetExponent(val),
sign == 0 ? BigNumberFlags.FlagNegative : 0);
}
private T PowerIntegral(
T thisValue,
EInteger powIntBig,
EContext ctx) {
int sign = powIntBig.signum();
T one = this.helper.ValueOf(1);
if (sign == 0) {
// however 0 to the power of 0 is undefined
return this.RoundToPrecision(one, ctx);
}
if (powIntBig.equals(EInteger.FromInt32(1))) {
return this.RoundToPrecision(thisValue, ctx);
}
if (powIntBig.compareTo(2) == 0) {
return this.Multiply(thisValue, thisValue, ctx);
}
if (powIntBig.compareTo(3) == 0) {
return this.Multiply(
thisValue,
this.Multiply(thisValue, thisValue, null),
ctx);
}
boolean retvalNeg = this.IsNegative(thisValue) && !powIntBig.isEven();
FastInteger error = this.helper.GetDigitLength(powIntBig.Abs());
error = error.Copy();
error.AddInt(18);
EInteger bigError = error.ToEInteger();
/*DUL("thisValue=" + thisValue +
" powInt=" + powIntBig);*/
EContext ctxdiv = ctx == null ? ctx : SetPrecisionIfLimited(
ctx,
ctx.getPrecision().Add(bigError))
.WithRounding(ERounding.HalfEven).WithBlankFlags();
if (sign < 0) {
// Use the reciprocal for negative powers
thisValue = this.Divide(one, thisValue, ctxdiv);
/*DUL("-->recip thisValue=" + thisValue +
" powInt=" + powIntBig + " flags=" + (ctxdiv == null ? -1 :
ctxdiv.getFlags()));*/
if ((ctxdiv.getFlags() & EContext.FlagOverflow) != 0) {
return this.SignalOverflow(ctx, retvalNeg);
}
powIntBig = powIntBig.Negate();
}
T r = one;
while (!powIntBig.isZero()) {
if (!powIntBig.isEven()) {
/*DUL("-->thisValue=" + thisValue +
" powInt=" + powIntBig + " flags=" +
(ctxdiv == null ? -1 : ctxdiv.getFlags()));*/
r = this.Multiply(r, thisValue, ctxdiv);
if (ctxdiv != null && (ctxdiv.getFlags() & EContext.FlagOverflow) != 0) {
return this.SignalOverflow(ctx, retvalNeg);
}
}
powIntBig = powIntBig.ShiftRight(1);
if (!powIntBig.isZero()) {
if (ctxdiv != null) {
ctxdiv.setFlags(ctxdiv.getFlags()&~(EContext.FlagOverflow));
}
T tmp = this.Multiply(thisValue, thisValue, ctxdiv);
if (ctxdiv != null && (ctxdiv.getFlags() & EContext.FlagOverflow) != 0) {
// Avoid multiplying too huge numbers with
// limited exponent range
return this.SignalOverflow(ctx, retvalNeg);
}
thisValue = tmp;
}
// System.out.println("r="+r);
}
if (ctx != null && ctx.getHasFlags()) {
ctx.setFlags(ctx.getFlags()|(ctxdiv.getFlags() & (
EContext.FlagUnderflow |
EContext.FlagSubnormal | EContext.FlagInexact |
EContext.FlagRounded | EContext.FlagClamped)));
}
return this.RoundToPrecision(r, ctx);
}
private T ReduceToPrecisionAndIdealExponent(
T thisValue,
EContext ctx,
FastInteger precision,
FastInteger idealExp) {
T ret = this.RoundToPrecision(thisValue, ctx);
if (ret != null && (this.helper.GetFlags(ret) &
BigNumberFlags.FlagSpecial) == 0) {
EInteger bigmant = this.helper.GetMantissa(ret);
FastInteger exp = FastInteger.FromBig(this.helper.GetExponent(ret));
int radix = this.thisRadix;
if (bigmant.isZero()) {
exp = new FastInteger(0);
} else {
FastInteger digits = (precision == null) ? null :
this.helper.GetDigitLength(bigmant);
bigmant = NumberUtility.ReduceTrailingZeros(
bigmant,
exp,
radix,
digits,
precision,
idealExp);
}
int flags = this.helper.GetFlags(thisValue);
ret = this.helper.CreateNewWithFlags(
bigmant,
exp.ToEInteger(),
flags);
if (ctx != null && ctx.getClampNormalExponents()) {
EContext ctxtmp = ctx.WithBlankFlags();
ret = this.RoundToPrecision(ret, ctxtmp);
if (ctx.getHasFlags()) {
ctx.setFlags(ctx.getFlags()|(ctxtmp.getFlags() & ~EContext.FlagClamped));
}
}
ret = this.EnsureSign(ret, (flags & BigNumberFlags.FlagNegative) != 0);
}
return ret;
}
private T RemainderHandleSpecial(
T thisValue,
T other,
EContext ctx) {
int thisFlags = this.helper.GetFlags(thisValue);
int otherFlags = this.helper.GetFlags(other);
if (((thisFlags | otherFlags) & BigNumberFlags.FlagSpecial) != 0) {
T result = this.HandleNotANumber(thisValue, other, ctx);
if ((Object)result != (Object)null) {
return result;
}
if ((thisFlags & BigNumberFlags.FlagInfinity) != 0) {
return this.SignalInvalid(ctx);
}
if ((otherFlags & BigNumberFlags.FlagInfinity) != 0) {
return this.RoundToPrecision(thisValue, ctx);
}
}
return this.helper.GetMantissa(other).isZero() ? this.SignalInvalid(ctx) :
null;
}
private T ReturnQuietNaN(T thisValue, EContext ctx) {
EInteger mant = this.helper.GetMantissa(thisValue);
boolean mantChanged = false;
if (!mant.isZero() && ctx != null && ctx.getHasMaxPrecision()) {
FastInteger compPrecision = FastInteger.FromBig(ctx.getPrecision());
if (this.helper.GetDigitLength(mant).compareTo(compPrecision) >= 0) {
// Mant's precision is higher than the maximum precision
EInteger limit = this.TryMultiplyByRadixPower(
EInteger.FromInt32(1),
compPrecision);
if (limit == null) {
// Limit can't be allocated
return this.SignalInvalidWithMessage(
ctx,
"Result requires too much memory");
}
if (mant.compareTo(limit) >= 0) {
mant = mant.Remainder(limit);
mantChanged = true;
}
}
}
int flags = this.helper.GetFlags(thisValue);
if (!mantChanged && (flags & BigNumberFlags.FlagQuietNaN) != 0) {
return thisValue;
}
flags &= BigNumberFlags.FlagNegative;
flags |= BigNumberFlags.FlagQuietNaN;
return this.helper.CreateNewWithFlags(mant, EInteger.FromInt32(0), flags);
}
private boolean IsNullOrInt32FriendlyContext(EContext ctx) {
return ctx == null || (
(!ctx.getHasFlags() && ctx.getTraps() == 0) &&
(!ctx.getHasExponentRange() ||
(ctx.getEMin().compareTo(-10) < 0 && ctx.getEMax().signum() >= 0)) &&
ctx.getRounding() != ERounding.Floor &&
(!ctx.getHasMaxPrecision() ||
(this.thisRadix >= 10 && !ctx.isPrecisionInBits() &&
ctx.getPrecision().compareTo(10) >= 0) ||
((this.thisRadix >= 2 || ctx.isPrecisionInBits()) &&
ctx.getPrecision().compareTo(32) >= 0)));
}
private boolean RoundGivenAccum(
IShiftAccumulator accum,
ERounding rounding,
boolean neg) {
boolean incremented = false;
int radix = this.thisRadix;
int lastDiscarded = accum.getLastDiscardedDigit();
int olderDiscarded = accum.getOlderDiscardedDigits();
// NOTE: HalfUp, HalfEven, and HalfDown care about
// the identity of the last discarded digit
if (rounding == ERounding.HalfUp) {
incremented |= lastDiscarded >= (radix / 2);
} else if (rounding == ERounding.HalfEven) {
if (lastDiscarded >= (radix / 2)) {
if (lastDiscarded > (radix / 2) || olderDiscarded != 0) {
incremented = true;
} else {
incremented |= accum.ShiftedIntMod(2) == 1;
}
}
} else if (rounding == ERounding.HalfDown) {
incremented |= lastDiscarded > (radix / 2) || (lastDiscarded ==
(radix / 2) && olderDiscarded != 0);
} else if (rounding == ERounding.Ceiling) {
incremented |= !neg && (lastDiscarded | olderDiscarded) != 0;
} else if (rounding == ERounding.Floor) {
incremented |= neg && (lastDiscarded | olderDiscarded) != 0;
} else if (rounding == ERounding.Up) {
incremented |= (lastDiscarded | olderDiscarded) != 0;
} else if (rounding == ERounding.Odd ||
(rounding == ERounding.OddOrZeroFiveUp && radix == 2)) {
incremented |= (lastDiscarded | olderDiscarded) != 0 &&
(accum.ShiftedIntMod(2) == 0);
} else if (rounding == ERounding.ZeroFiveUp ||
(rounding == ERounding.OddOrZeroFiveUp && radix != 2)) {
if ((lastDiscarded | olderDiscarded) != 0) {
if (radix == 2) {
incremented = true;
} else {
int lastDigit = accum.ShiftedIntMod(radix);
if (lastDigit == 0 || lastDigit == (radix / 2)) {
incremented = true;
}
}
}
}
return incremented;
}
private boolean RoundGivenDigits(
int lastDiscarded,
int olderDiscarded,
ERounding rounding,
boolean neg,
FastInteger fastNumber) {
boolean incremented = false;
int radix = this.thisRadix;
// NOTE: HalfUp, HalfEven, and HalfDown care about
// the identity of the last discarded digit
if (rounding == ERounding.HalfUp) {
incremented |= lastDiscarded >= (radix / 2);
} else if (rounding == ERounding.HalfEven) {
if (lastDiscarded >= (radix / 2)) {
if (lastDiscarded > (radix / 2) || olderDiscarded != 0) {
incremented = true;
} else {
incremented |= !fastNumber.isEvenNumber();
}
}
} else if (rounding == ERounding.HalfDown) {
incremented |= lastDiscarded > (radix / 2) || (lastDiscarded ==
(radix / 2) && olderDiscarded != 0);
} else if (rounding == ERounding.Ceiling) {
incremented |= !neg && (lastDiscarded | olderDiscarded) != 0;
} else if (rounding == ERounding.Floor) {
incremented |= neg && (lastDiscarded | olderDiscarded) != 0;
} else if (rounding == ERounding.Up) {
incremented |= (lastDiscarded | olderDiscarded) != 0;
} else if (rounding == ERounding.Odd ||
(rounding == ERounding.OddOrZeroFiveUp && radix == 2)) {
incremented |= (lastDiscarded | olderDiscarded) != 0 &&
fastNumber.isEvenNumber();
} else if (rounding == ERounding.ZeroFiveUp ||
(rounding == ERounding.OddOrZeroFiveUp && radix != 2)) {
if ((lastDiscarded | olderDiscarded) != 0) {
if (radix == 2) {
incremented = true;
} else {
int lastDigit = FastIntegerFixed.FromFastInteger(
fastNumber).Mod(radix);
if (lastDigit == 0 || lastDigit == (radix / 2)) {
incremented = true;
}
}
}
}
return incremented;
}
private boolean RoundGivenDigits(
int lastDiscarded,
int olderDiscarded,
ERounding rounding,
boolean neg,
long longNumber) {
boolean incremented = false;
int radix = this.thisRadix;
// NOTE: HalfUp, HalfEven, and HalfDown care about
// the identity of the last discarded digit
if (rounding == ERounding.HalfUp) {
incremented |= lastDiscarded >= (radix / 2);
} else if (rounding == ERounding.HalfEven) {
if (lastDiscarded >= (radix / 2)) {
if (lastDiscarded > (radix / 2) || olderDiscarded != 0) {
incremented = true;
} else {
incremented |= (longNumber & 1) != 0;
}
}
} else if (rounding == ERounding.HalfDown) {
incremented |= lastDiscarded > (radix / 2) || (lastDiscarded ==
(radix / 2) && olderDiscarded != 0);
} else if (rounding == ERounding.Ceiling) {
incremented |= !neg && (lastDiscarded | olderDiscarded) != 0;
} else if (rounding == ERounding.Floor) {
incremented |= neg && (lastDiscarded | olderDiscarded) != 0;
} else if (rounding == ERounding.Up) {
incremented |= (lastDiscarded | olderDiscarded) != 0;
} else if (rounding == ERounding.Odd ||
(rounding == ERounding.OddOrZeroFiveUp && radix == 2)) {
incremented |= (lastDiscarded | olderDiscarded) != 0 &&
((longNumber & 1) == 0);
} else if (rounding == ERounding.ZeroFiveUp ||
(rounding == ERounding.OddOrZeroFiveUp && radix != 2)) {
if ((lastDiscarded | olderDiscarded) != 0) {
if (radix == 2) {
incremented = true;
} else {
int lastDigit = (int)(longNumber % radix);
if (lastDigit == 0 || lastDigit == (radix / 2)) {
incremented = true;
}
}
}
}
return incremented;
}
private static final EContext DefaultUnlimited =
EContext.UnlimitedHalfEven.WithRounding(ERounding.HalfEven);
// private static int compareFast = 0;
// private static int compareSlow = 0;
// private static int compareNone = 0;
private T RoundToPrecisionInternal(
T thisValue,
int lastDiscarded,
int olderDiscarded,
FastInteger shift,
boolean adjustNegativeZero,
EContext ctx) {
boolean mantissaWasZero;
boolean nonHalfRounding;
boolean finalizing = false;
int flags;
boolean unlimitedPrecisionExp = ctx == null ||
(!ctx.getHasMaxPrecision() && !ctx.getHasExponentRange());
int thisFlags = this.helper.GetFlags(thisValue);
if ((thisFlags & BigNumberFlags.FlagSpecial) != 0) {
if ((thisFlags & BigNumberFlags.FlagSignalingNaN) != 0) {
if (ctx != null && ctx.getHasFlags()) {
ctx.setFlags(ctx.getFlags()|(EContext.FlagInvalid));
}
return this.ReturnQuietNaN(thisValue, ctx);
}
if ((thisFlags & BigNumberFlags.FlagQuietNaN) != 0) {
return this.ReturnQuietNaN(thisValue, ctx);
}
if ((thisFlags & BigNumberFlags.FlagInfinity) != 0) {
return thisValue;
}
}
// If context has unlimited precision and exponent range,
// and no discarded digits or shifting
if (unlimitedPrecisionExp && (lastDiscarded | olderDiscarded) == 0 &&
(shift == null || shift.isValueZero())) {
if (!(adjustNegativeZero &&
(thisFlags & BigNumberFlags.FlagNegative) != 0 &&
this.helper.GetMantissa(thisValue).isZero())) {
return thisValue;
}
}
// If context has unlimited precision and exponent range,
// and no flags, traps, or shifting
if (unlimitedPrecisionExp && (ctx == null || !ctx.getHasFlagsOrTraps()) &&
(shift == null || shift.isValueZero())) {
ERounding er = (ctx == null) ? ERounding.HalfDown : ctx.getRounding();
boolean negative = (thisFlags & BigNumberFlags.FlagNegative) != 0;
boolean negzero = adjustNegativeZero && negative &&
this.helper.GetMantissa(thisValue).isZero() &&
(er != ERounding.Floor);
if (!negzero) {
if (er == ERounding.Down) {
return thisValue;
}
if (this.thisRadix == 10 && er == ERounding.HalfEven) {
if (lastDiscarded < 5) {
return thisValue;
} else if (lastDiscarded > 5 || olderDiscarded != 0) {
FastIntegerFixed bm = this.helper.GetMantissaFastInt(thisValue);
return this.helper.CreateNewWithFlagsFastInt(
FastIntegerFixed.Add(bm, FastIntegerFixed.One),
this.helper.GetExponentFastInt(thisValue),
thisFlags);
}
}
if (this.thisRadix == 2 && (er == ERounding.HalfEven) &&
lastDiscarded == 0) {
return thisValue;
}
if (!this.RoundGivenDigits(
lastDiscarded,
olderDiscarded,
er,
negative,
FastInteger.FromBig(this.helper.GetMantissa(thisValue)))) {
return thisValue;
} else {
FastIntegerFixed bm = this.helper.GetMantissaFastInt(thisValue);
return this.helper.CreateNewWithFlagsFastInt(
FastIntegerFixed.Add(bm, FastIntegerFixed.One),
this.helper.GetExponentFastInt(thisValue),
thisFlags);
}
}
}
if ((lastDiscarded | olderDiscarded) == 0 &&
(shift == null || shift.isValueZero())) {
// System.out.println("fastpath for "+ctx+", "+thisValue);
FastIntegerFixed expabs = this.helper.GetExponentFastInt(thisValue);
if (expabs.isValueZero() && this.IsNullOrInt32FriendlyContext(ctx)) {
FastIntegerFixed mantabs = this.helper.GetMantissaFastInt(thisValue);
if (mantabs.isValueZero() && adjustNegativeZero &&
(thisFlags & BigNumberFlags.FlagNegative) != 0) {
return this.helper.ValueOf(0);
}
if (mantabs.CanFitInInt32()) {
// compareFast++;
return thisValue;
}
}
}
ctx = (ctx == null) ? (DefaultUnlimited) : ctx;
ERounding rounding = ctx.getRounding();
boolean neg;
// Check for common binary floating-point contexts, including those
// covered by Binary32 and Binary64.
// In the precision check below, no need to check if precision is
// less than 0, since the EContext Object should already ensure this
if (this.thisRadix == 2 && ctx.getHasMaxPrecision() && ctx.getHasExponentRange() &&
ctx.getPrecision().compareTo(53) <= 0 && ctx.getEMin().compareTo(-2000) >= 0 &&
ctx.getEMax().compareTo(2000) < 0 && (shift == null ||
shift.CompareToInt(64) < 0)) {
int intPrecision = ctx.getPrecision().ToInt32Checked();
int intEMax = ctx.getEMax().ToInt32Checked();
int intEMin = ctx.getEMin().ToInt32Checked();
int intShift = (shift == null) ? 0 : shift.ToInt32();
FastIntegerFixed fmant = this.helper.GetMantissaFastInt(thisValue);
FastIntegerFixed fexp = this.helper.GetExponentFastInt(thisValue);
if (fmant.CanFitInInt64() && fexp.CanFitInInt32()) {
long mantlong = fmant.ToInt64();
int explong = fexp.ToInt32();
int adjustedExp;
int normalMin;
int intDigitCount;
long origmant = mantlong;
// get the exponent range
// Fast path to check if rounding is necessary at all
// NOTE: At this point, the number won't be infinity or NaN
if (shift == null || shift.isValueZero()) {
if (adjustNegativeZero && (thisFlags &
BigNumberFlags.FlagNegative) !=
0 && mantlong == 0 && (ctx.getRounding() != ERounding.Floor)) {
// Change negative zero to positive zero
// except if the rounding mode is Floor
thisValue = this.EnsureSign(thisValue, false);
thisFlags = 0;
}
intDigitCount = NumberUtility.BitLength(mantlong);
if (intDigitCount < intPrecision) {
boolean withRounding = false;
boolean stillWithinPrecision = false;
if (ctx.getHasFlags() && (lastDiscarded | olderDiscarded) != 0) {
ctx.setFlags(ctx.getFlags()|(EContext.FlagInexact | EContext.FlagRounded));
}
// NOTE: accum includes lastDiscarded and olderDiscarded
if (!this.RoundGivenDigits(
lastDiscarded,
olderDiscarded,
ctx.getRounding(),
(thisFlags & BigNumberFlags.FlagNegative) != 0,
mantlong)) {
stillWithinPrecision = true;
} else {
withRounding = true;
++mantlong;
intDigitCount = NumberUtility.BitLength(mantlong);
if (intDigitCount < intPrecision ||
(intDigitCount == intPrecision && (this.thisRadix & 1) == 0 &&
(mantlong & 1) != 0)) {
stillWithinPrecision = true;
} else {
long radixPower = 1L << intPrecision;
stillWithinPrecision = mantlong < radixPower;
}
}
if (stillWithinPrecision) {
if (!ctx.getHasExponentRange()) {
return withRounding ? this.helper.CreateNewWithFlagsFastInt(
FastIntegerFixed.FromInt64(mantlong),
this.helper.GetExponentFastInt(thisValue),
thisFlags) : thisValue;
}
int bigexp = explong;
if (ctx == null || ctx.getAdjustExponent()) {
adjustedExp = bigexp + intPrecision - 1;
normalMin = intEMin + intPrecision - 1;
} else {
adjustedExp = bigexp;
normalMin = intEMin;
}
if (adjustedExp <= intEMax && adjustedExp >= normalMin) {
return withRounding ? this.helper.CreateNewWithFlagsFastInt(
FastIntegerFixed.FromInt64(mantlong),
FastIntegerFixed.FromInt32(bigexp),
thisFlags) : thisValue;
}
}
}
}
// Relatively slow path follows
// System.out.println("slowpath mantexp="+mantlong+","+explong);
neg = (thisFlags & BigNumberFlags.FlagNegative) != 0;
if (adjustNegativeZero && neg && (ctx.getRounding() != ERounding.Floor) &&
origmant == 0) {
// Change negative zero to positive zero
// except if the rounding mode is Floor
thisValue = this.EnsureSign(thisValue, false);
thisFlags = 0;
neg = false;
}
mantissaWasZero = mantlong == 0 &&
(lastDiscarded | olderDiscarded) == 0;
flags = 0;
intDigitCount = NumberUtility.BitLength(mantlong);
nonHalfRounding = rounding != ERounding.HalfEven &&
rounding != ERounding.HalfUp && rounding != ERounding.HalfDown;
int intDiscardedBits = 0;
if (intDigitCount > intPrecision) {
int bitShift = intDigitCount - intPrecision;
intDiscardedBits += bitShift;
olderDiscarded |= lastDiscarded;
// Get the bottommost shift minus 1 bits
olderDiscarded |= (bitShift > 1 && (mantlong <<
(64 - bitShift + 1)) != 0) ? 1 : 0;
// Get the bit just above that bit
lastDiscarded = (int)((mantlong >> (bitShift - 1)) & 0x01);
olderDiscarded = (olderDiscarded != 0) ? 1 : 0;
mantlong >>= bitShift;
} else if (intShift > 0 && mantlong != 0) {
olderDiscarded |= lastDiscarded;
if (intShift > intDigitCount) {
olderDiscarded |= 1;
lastDiscarded = 0;
mantlong = 0;
} else {
// bottommost intShift minus 1 bits
olderDiscarded |= (intShift > 1 && (mantlong <<
(64 - intShift + 1)) != 0) ? 1 : 0;
lastDiscarded = (int)((mantlong >> (intShift - 1)) & 0x01);
mantlong >>= intShift;
}
intDiscardedBits += intShift;
}
explong += intDiscardedBits;
long intFinalMantissa = 0;
int intFinalExponent = 0;
finalizing = false;
adjustedExp = explong;
if (ctx.getAdjustExponent()) {
adjustedExp += NumberUtility.BitLength(mantlong) - 1;
}
if (adjustedExp > intEMax) {
if (mantissaWasZero) {
if (ctx.getHasFlags()) {
ctx.setFlags(ctx.getFlags()|(flags | EContext.FlagClamped));
}
if (ctx.getClampNormalExponents() && ctx.getAdjustExponent()) {
// Clamp exponents to eMax + 1 - precision
// if directed
intEMax = Math.min(intEMax, intEMax + 1 - intPrecision);
}
return this.helper.CreateNewWithFlagsFastInt(
FastIntegerFixed.FromInt64(mantlong),
FastIntegerFixed.FromInt32(intEMax),
thisFlags);
}
return this.SignalOverflow(ctx, neg);
} else if (adjustedExp < intEMin) {
// Subnormal
int etiny = intEMin;
if (ctx.getAdjustExponent()) {
etiny -= intPrecision - 1;
}
if (ctx.getHasFlags() && mantlong != 0) {
int newAdjExponent = adjustedExp;
if (this.RoundGivenDigits(
lastDiscarded,
olderDiscarded,
rounding,
neg,
mantlong)) {
long intEarlyRounded = mantlong + 1;
if ((intEarlyRounded >> intPrecision) != 0) {
newAdjExponent = ctx.getAdjustExponent() ?
explong + intPrecision - 1 : explong;
} else {
newAdjExponent = ctx.getAdjustExponent() ?
explong + NumberUtility.BitLength(intEarlyRounded) - 1 :
explong;
}
}
if (newAdjExponent < intEMin) {
flags |= EContext.FlagSubnormal;
}
}
int subExp = explong;
if (subExp < etiny) {
int intExpDiff = etiny - subExp;
intDigitCount = NumberUtility.BitLength(mantlong);
if (mantlong != 0) {
olderDiscarded |= lastDiscarded;
if (intExpDiff > intDigitCount) {
olderDiscarded |= 1;
lastDiscarded = 0;
mantlong = 0;
} else {
// bottommost expdiff minus 1 bits
olderDiscarded |= (intExpDiff > 1 && (mantlong <<
(64 - intExpDiff + 1)) != 0) ? 1 : 0;
lastDiscarded = (int)((mantlong >> (intExpDiff - 1)) & 0x01);
mantlong >>= intExpDiff;
}
intDiscardedBits += intExpDiff;
}
/* System.out.println("mantlong now {0}, ld={1}, od={2} [ed={3},
flags={4}]",EInteger.FromInt64(mantlong).ToRadixString(2),
lastDiscarded,
olderDiscarded, expdiff, flags);
*/
boolean nonZeroDiscardedDigits = (lastDiscarded | olderDiscarded) !=
0;
if (intDiscardedBits > 0 || nonZeroDiscardedDigits) {
if (!mantissaWasZero) {
flags |= EContext.FlagRounded;
}
if (nonZeroDiscardedDigits) {
flags |= EContext.FlagInexact | EContext.FlagRounded;
if (rounding == ERounding.None) {
return this.SignalInvalidWithMessage(
ctx,
"Rounding was required");
}
}
if (this.RoundGivenDigits(
lastDiscarded,
olderDiscarded,
rounding,
neg,
mantlong)) {
++mantlong;
}
}
if (ctx.getHasFlags()) {
if (mantlong == 0) {
flags |= EContext.FlagClamped;
}
if ((flags & (EContext.FlagSubnormal |
EContext.FlagInexact)) == (EContext.FlagSubnormal |
EContext.FlagInexact)) {
flags |= EContext.FlagUnderflow |
EContext.FlagRounded;
}
}
// Finalize result of rounding operation
intFinalMantissa = mantlong;
intFinalExponent = etiny;
finalizing = true;
}
}
if (!finalizing) {
boolean recheckOverflow = false;
boolean doRounding = intDiscardedBits != 0 ||
(lastDiscarded | olderDiscarded) != 0;
if (doRounding) {
if (mantlong != 0) {
flags |= EContext.FlagRounded;
}
if ((lastDiscarded | olderDiscarded) != 0) {
flags |= EContext.FlagInexact | EContext.FlagRounded;
if (rounding == ERounding.None) {
return this.SignalInvalidWithMessage(
ctx,
"Rounding was required");
}
}
if (this.RoundGivenDigits(
lastDiscarded,
olderDiscarded,
rounding,
neg,
mantlong)) {
++mantlong;
// Check if mantissa's precision is now greater
// than the one set by the context
if ((mantlong >> intPrecision) != 0) {
mantlong >>= 1;
++explong;
recheckOverflow = true;
}
}
}
if (recheckOverflow) {
// Check for overflow again
adjustedExp = explong;
if (ctx.getAdjustExponent()) {
adjustedExp += NumberUtility.BitLength(mantlong) - 1;
}
if (adjustedExp > intEMax) {
return this.SignalOverflow(ctx, neg);
}
}
intFinalMantissa = mantlong;
intFinalExponent = explong;
}
if (ctx.getClampNormalExponents()) {
// Clamp exponents to eMax + 1 - precision
// if directed
int clampExp = intEMax;
if (ctx.getAdjustExponent()) {
clampExp += intPrecision - 1;
}
if (intFinalExponent > clampExp) {
if (intFinalMantissa != 0) {
int expdiff = intFinalExponent - clampExp;
// System.out.println("Clamping " + exp + " to " + clampExp);
intFinalMantissa <<= expdiff;
}
if (ctx.getHasFlags()) {
flags |= EContext.FlagClamped;
}
intFinalExponent = clampExp;
}
}
if (ctx.getHasFlags()) {
ctx.setFlags(ctx.getFlags()|(flags));
}
return this.helper.CreateNewWithFlagsFastInt(
FastIntegerFixed.FromInt64(intFinalMantissa),
FastIntegerFixed.FromInt64(intFinalExponent),
neg ? BigNumberFlags.FlagNegative : 0);
}
}
// Binary precision of potentially non-binary numbers
// binaryPrec means whether precision is the number of bits and not
// digits
boolean binaryPrec = ctx.isPrecisionInBits() && this.thisRadix != 2 &&
!ctx.getPrecision().isZero();
// get the precision
FastInteger fastPrecision = FastInteger.FromBig(ctx.getPrecision());
IShiftAccumulator accum = null;
FastInteger fastAdjustedExp;
FastInteger fastNormalMin;
FastInteger fastEMin = null;
FastInteger fastEMax = null;
FastIntegerFixed fastEMaxFixed = null;
// get the exponent range
if (ctx != null && ctx.getHasExponentRange()) {
fastEMax = FastInteger.FromBig(ctx.getEMax());
fastEMaxFixed = FastIntegerFixed.FromBig(ctx.getEMax());
fastEMin = FastInteger.FromBig(ctx.getEMin());
}
boolean unlimitedPrec = !ctx.getHasMaxPrecision();
if (!binaryPrec) {
// Fast path to check if rounding is necessary at all
// NOTE: At this point, the number won't be infinity or NaN
if (!unlimitedPrec && (shift == null || shift.isValueZero())) {
FastIntegerFixed mantabs = this.helper.GetMantissaFastInt(
thisValue);
if (adjustNegativeZero && (thisFlags & BigNumberFlags.FlagNegative) !=
0 && mantabs.isValueZero() && (ctx.getRounding() != ERounding.Floor)) {
// Change negative zero to positive zero
// except if the rounding mode is Floor
thisValue = this.EnsureSign(thisValue, false);
thisFlags = 0;
}
accum = this.helper.CreateShiftAccumulatorWithDigitsFastInt(
mantabs,
lastDiscarded,
olderDiscarded);
FastInteger estDigitCount = accum.OverestimateDigitLength();
// NOTE: Overestimating the digit count will catch most,
// but not all, numbers that fit fastPrecision, and will not
// catch any numbers that don't fit fastPrecision
// System.out.println("estDigitCount=" + estDigitCount + ", " +
// fastPrecision);
if (estDigitCount.compareTo(fastPrecision) <= 0) {
boolean withRounding = false;
boolean stillWithinPrecision = false;
if (ctx.getHasFlags() && (lastDiscarded | olderDiscarded) != 0) {
ctx.setFlags(ctx.getFlags()|(EContext.FlagInexact | EContext.FlagRounded));
}
if (!this.RoundGivenAccum(
accum,
ctx.getRounding(),
(thisFlags & BigNumberFlags.FlagNegative) != 0)) {
stillWithinPrecision = true;
} else {
withRounding = true;
mantabs = mantabs.Increment();
FastInteger digitCount = accum.GetDigitLength();
int precisionCmp = digitCount.compareTo(fastPrecision);
if (precisionCmp < 0 ||
(precisionCmp == 0 && (this.thisRadix & 1) == 0 &&
!mantabs.isEvenNumber())) {
stillWithinPrecision = true;
} else {
EInteger radixPower =
this.TryMultiplyByRadixPower(EInteger.FromInt32(1), fastPrecision);
// System.out.println("now " + mantabs + "," + fastPrecision);
if (radixPower == null) {
return this.SignalInvalidWithMessage(
ctx,
"Result requires too much memory");
}
stillWithinPrecision = mantabs.compareTo(radixPower) <
0;
}
}
if (stillWithinPrecision) {
if (!ctx.getHasExponentRange()) {
return withRounding ? this.helper.CreateNewWithFlagsFastInt(
mantabs,
this.helper.GetExponentFastInt(thisValue),
thisFlags) : thisValue;
}
FastIntegerFixed bigexp =
this.helper.GetExponentFastInt(thisValue);
if (ctx == null || ctx.getAdjustExponent()) {
fastAdjustedExp = bigexp.ToFastInteger()
.Add(fastPrecision).Decrement();
fastNormalMin = fastEMin.Copy()
.Add(fastPrecision).Decrement();
} else {
fastAdjustedExp = bigexp.ToFastInteger();
fastNormalMin = fastEMin;
}
// System.out.println("{0}->{1},{2}"
// , fastAdjustedExp, fastEMax, fastNormalMin);
if (fastAdjustedExp.compareTo(fastEMax) <= 0 &&
fastAdjustedExp.compareTo(fastNormalMin) >= 0) {
return withRounding ? this.helper.CreateNewWithFlagsFastInt(
mantabs,
bigexp,
thisFlags) : thisValue;
}
}
}
}
}
// compareSlow++;
neg = (thisFlags & BigNumberFlags.FlagNegative) != 0;
if (adjustNegativeZero && neg && (rounding != ERounding.Floor) &&
this.helper.GetMantissa(thisValue).isZero()) {
// Change negative zero to positive zero
// except if the rounding mode is Floor
thisValue = this.EnsureSign(thisValue, false);
thisFlags = 0;
neg = false;
}
FastIntegerFixed bigmantissa = this.helper.GetMantissaFastInt(thisValue);
mantissaWasZero = bigmantissa.isValueZero() && (lastDiscarded |
olderDiscarded) == 0;
FastIntegerFixed expfixed = this.helper.GetExponentFastInt(thisValue);
FastInteger exp = expfixed.ToFastInteger();
flags = 0;
if (accum == null) {
accum = this.helper.CreateShiftAccumulatorWithDigitsFastInt(
bigmantissa,
lastDiscarded,
olderDiscarded);
}
FastInteger bitLength = fastPrecision;
if (binaryPrec) {
fastPrecision =
this.DigitLengthUpperBoundForBitPrecision(fastPrecision);
}
nonHalfRounding = rounding != ERounding.HalfEven &&
rounding != ERounding.HalfUp && rounding != ERounding.HalfDown;
if (ctx != null && ctx.getHasMaxPrecision() &&
ctx.getHasExponentRange()) {
long estMantDigits = bigmantissa.CanFitInInt32() ?
10 : bigmantissa.ToEInteger().GetUnsignedBitLengthAsInt64();
if (estMantDigits > 128) {
// Get bounds on stored precision
FastIntegerFixed[] bounds = NumberUtility.DigitLengthBoundsFixed(
this.helper,
bigmantissa);
FastIntegerFixed lowExpBound = expfixed;
if (ctx.getAdjustExponent()) {
lowExpBound = lowExpBound.Add(bounds[0]).Subtract(2);
}
FastIntegerFixed highExpBound = expfixed;
highExpBound = highExpBound.Add(bounds[1]);
FastIntegerFixed fpf = FastIntegerFixed.FromFastInteger(fastPrecision);
/*
String ch1=""+lowExpBound;ch1=ch1.substring(0,Math.min(12,ch1.length()));
String ch2=""+highExpBound;ch2=ch2.substring(0,Math.min(12,ch2.length()));
System.out.println("exp="+expfixed);
System.out.println("bounds="+ch1+"/"+ch2+"/"+fastEMax+
" fpf="+fastPrecision + " highexp=" +highExpBound.Add(fpf).Add(4));
*/
if (lowExpBound.compareTo(fastEMax) > 0) {
// Overflow.
return this.SignalOverflow(ctx, neg);
}
FastIntegerFixed underflowBound = highExpBound.Add(fpf).Add(4);
// FastIntegerFixed underflowBound2 = highExpBound.Add(bounds[1]).Add(4);
// if (underflowBound2.compareTo(underflowBound) > 0) {
// underflowBound = underflowBound2;
// }
// System.out.println("underflowBound="+underflowBound);
if (underflowBound.compareTo(fastEMin) < 0) {
// Underflow.
// NOTE: Due to estMantDigits check
// above, we know significand is neither zero nor 1(
// SignalUnderflow will pass significands of 0 or 1 to
// RoundToPrecision).
return this.SignalUnderflow(ctx, neg, false);
}
/*
System.out.println("mantbits=" +
bigmantissa.ToEInteger().GetUnsignedBitLengthAsInt64() +
" shift=" + shift + " fastprec=" + fastPrecision +
" expbits=" + exp.ToEInteger().GetUnsignedBitLengthAsInt64() +
" expsign=" + exp.ToEInteger().compareTo(0)); */
}
}
if (!unlimitedPrec) {
accum.ShiftToDigits(fastPrecision, shift, nonHalfRounding);
} else {
if (shift != null && shift.signum() != 0) {
accum.TruncateOrShiftRight(shift, nonHalfRounding);
}
fastPrecision = accum.GetDigitLength();
}
if (binaryPrec) {
while (bitLength.compareTo(
accum.getShiftedInt().GetUnsignedBitLengthAsEInteger()) < 0) {
accum.ShiftRightInt(1);
}
}
FastInteger discardedBits = accum.getDiscardedDigitCount().Copy();
exp.Add(discardedBits);
FastIntegerFixed finalMantissa = null;
FastIntegerFixed finalExponent = null;
finalizing = false;
FastInteger adjExponent;
adjExponent = ctx.getAdjustExponent() ?
exp.Copy().Add(accum.GetDigitLength()).Decrement() : exp.Copy();
if (binaryPrec) {
// NOTE: Binary precision case only
if (fastEMax != null && adjExponent.compareTo(fastEMax) == 0) {
// May or may not be an overflow depending on the mantissa
FastInteger expdiff =
fastPrecision.Copy().Subtract(accum.GetDigitLength());
EInteger currMantissa = accum.getShiftedInt();
currMantissa = this.TryMultiplyByRadixPower(currMantissa, expdiff);
if (currMantissa == null) {
return this.SignalInvalidWithMessage(
ctx,
"Result requires too much memory");
}
if (bitLength.compareTo(
currMantissa.GetUnsignedBitLengthAsEInteger()) < 0) {
// Mantissa too high, treat as overflow
adjExponent.Increment();
}
}
}
if (fastEMax != null && adjExponent.compareTo(fastEMax) > 0) {
if (mantissaWasZero) {
if (ctx.getHasFlags()) {
ctx.setFlags(ctx.getFlags()|(flags | EContext.FlagClamped));
}
if (ctx.getClampNormalExponents() && ctx.getAdjustExponent()) {
// Clamp exponents to eMax + 1 - precision
// if directed
FastInteger clampExp = fastEMax.Copy();
clampExp.Increment().Subtract(fastPrecision);
if (fastEMax.compareTo(clampExp) > 0) {
fastEMax = clampExp;
}
}
return this.helper.CreateNewWithFlagsFastInt(
bigmantissa,
FastIntegerFixed.FromFastInteger(fastEMax),
thisFlags);
}
return this.SignalOverflow(ctx, neg);
} else if (fastEMin != null && adjExponent.compareTo(fastEMin) < 0) {
// Subnormal
FastInteger fastETiny = fastEMin;
if (ctx.getAdjustExponent()) {
fastETiny = fastETiny.Copy().Subtract(fastPrecision).Increment();
}
if (ctx.getHasFlags() && !accum.getShiftedInt().isZero()) {
FastInteger newAdjExponent = adjExponent;
if (this.RoundGivenAccum(accum, rounding, neg)) {
EInteger earlyRounded = accum.getShiftedInt().Add(EInteger.FromInt32(1));
if (!unlimitedPrec && (earlyRounded.isEven() ||
(this.thisRadix & 1) != 0)) {
FastInteger newDigitLength =
this.helper.GetDigitLength(earlyRounded);
// Ensure newDigitLength doesn't exceed precision
if (binaryPrec || newDigitLength.compareTo(fastPrecision) >
0) {
newDigitLength = fastPrecision.Copy();
}
newAdjExponent = ctx.getAdjustExponent() ?
exp.Copy().Add(newDigitLength).Decrement() : exp;
}
}
if (newAdjExponent.compareTo(fastEMin) < 0) {
// System.out.println("subnormal");
flags |= EContext.FlagSubnormal;
}
}
// System.out.println("exp=" + exp + " eTiny=" + fastETiny);
FastInteger subExp = exp.Copy();
// System.out.println("exp=" + subExp + " eTiny=" + fastETiny);
if (subExp.compareTo(fastETiny) < 0) {
// System.out.println("Less than ETiny");
FastInteger expdiff = fastETiny.Copy().Subtract(subExp);
// System.out.println("=
0)) {
accum = this.helper.CreateShiftAccumulatorWithDigits(
bigmantissaEInteger,
0,
0);
FastInteger newDigitLength = accum.GetDigitLength();
if (binaryPrec || newDigitLength.compareTo(fastPrecision) > 0) {
FastInteger neededShift =
newDigitLength.Copy().Subtract(fastPrecision);
accum.TruncateOrShiftRight(
neededShift,
nonHalfRounding);
if (binaryPrec) {
while (bitLength.compareTo(
accum.getShiftedInt().GetUnsignedBitLengthAsEInteger()) < 0) {
accum.ShiftRightInt(1);
}
}
if (accum.getDiscardedDigitCount().signum() != 0) {
exp.Add(accum.getDiscardedDigitCount());
discardedBits.Add(accum.getDiscardedDigitCount());
bigmantissaEInteger = accum.getShiftedInt();
recheckOverflow |= !binaryPrec;
}
}
}
}
}
if (fastEMax != null && recheckOverflow) {
// Check for overflow again
// System.out.println("recheck overflow2 {0} {1} / {2}"
// , adjExponent, fastEMax, accum.getShiftedInt());
adjExponent = exp.Copy();
if (ctx.getAdjustExponent()) {
adjExponent.Add(accum.GetDigitLength()).Decrement();
}
if (binaryPrec && fastEMax != null &&
adjExponent.compareTo(fastEMax) == 0) {
// May or may not be an overflow depending on the mantissa
// (uses accumulator from previous steps, including the check
// if the mantissa now exceeded the precision)
FastInteger expdiff =
fastPrecision.Copy().Subtract(accum.GetDigitLength());
EInteger currMantissa = accum.getShiftedInt();
currMantissa = this.TryMultiplyByRadixPower(currMantissa, expdiff);
if (currMantissa == null) {
return this.SignalInvalidWithMessage(
ctx,
"Result requires too much memory");
}
if (bitLength.compareTo(
currMantissa.GetUnsignedBitLengthAsEInteger()) < 0) {
// Mantissa too high, treat as overflow
adjExponent.Increment();
}
}
if (adjExponent.compareTo(fastEMax) > 0) {
return this.SignalOverflow(ctx, neg);
}
}
finalMantissa = FastIntegerFixed.FromBig(bigmantissaEInteger);
finalExponent = FastIntegerFixed.FromFastInteger(exp);
}
// Finalize the result of the rounding operation
if (ctx.getClampNormalExponents()) {
// Clamp exponents to eMax + 1 - precision
// if directed
FastInteger clampExp = fastEMax.Copy();
if (ctx.getAdjustExponent()) {
clampExp.Increment().Subtract(fastPrecision);
}
if (exp.compareTo(clampExp) > 0) {
if (!finalMantissa.isValueZero()) {
FastIntegerFixed expdiff = FastIntegerFixed.Subtract(
finalExponent,
FastIntegerFixed.FromFastInteger(clampExp));
// System.out.println("Clamping " + exp + " to " + clampExp);
finalMantissa = this.TryMultiplyByRadixPowerFastInt(
finalMantissa,
expdiff);
if (finalMantissa == null) {
return this.SignalInvalidWithMessage(
ctx,
"Result requires too much memory");
}
}
if (ctx.getHasFlags()) {
flags |= EContext.FlagClamped;
}
finalExponent = FastIntegerFixed.FromFastInteger(clampExp);
}
}
if (ctx.getHasFlags()) {
ctx.setFlags(ctx.getFlags()|(flags));
}
return this.helper.CreateNewWithFlagsFastInt(
finalMantissa,
finalExponent,
neg ? BigNumberFlags.FlagNegative : 0);
}
// Compare bigLeft with half of toCompareWith, while avoiding
// the need to compute half of toCompareWith in many cases.
// Assumes both inputs are positive.
private static int CompareToHalf(EInteger bigLeft, EInteger toCompareWith) {
long a = bigLeft.GetUnsignedBitLengthAsInt64();
long b = toCompareWith.GetUnsignedBitLengthAsInt64();
if (a != Long.MAX_VALUE && b != Long.MAX_VALUE) {
if (b - 1 > a) {
return -1;
}
if (a - 1 > b) {
return 1;
}
}
int cmp = bigLeft.compareTo(toCompareWith.ShiftRight(1));
return (cmp == 0 && !toCompareWith.isEven()) ? -1 : cmp;
}
private T RoundToScale(
EInteger mantissa,
EInteger remainder,
EInteger divisor,
EInteger desiredExponent,
FastInteger shift,
boolean neg,
EContext ctx) {
ERounding rounding = (ctx == null) ? ERounding.HalfEven : ctx.getRounding();
int lastDiscarded = 0;
int olderDiscarded = 0;
if (!remainder.isZero()) {
if (rounding == ERounding.HalfDown || rounding == ERounding.HalfUp ||
rounding == ERounding.HalfEven) {
int cmpHalf = CompareToHalf(remainder, divisor);
if (cmpHalf == 0) {
// remainder is exactly half
lastDiscarded = this.thisRadix / 2;
olderDiscarded = 0;
} else if (cmpHalf > 0) {
// remainder is greater than half
lastDiscarded = this.thisRadix / 2;
olderDiscarded = 1;
} else {
// remainder is less than half
lastDiscarded = 0;
olderDiscarded = 1;
}
} else {
// Rounding mode doesn't care about
// whether remainder is exactly half
if (rounding == ERounding.None) {
return this.SignalInvalidWithMessage(
ctx,
"Rounding was required");
}
lastDiscarded = 1;
olderDiscarded = 1;
}
}
int flags = 0;
EInteger newmantissa = mantissa;
if (shift.isValueZero()) {
if ((lastDiscarded | olderDiscarded) != 0) {
flags |= EContext.FlagInexact | EContext.FlagRounded;
if (rounding == ERounding.None) {
return this.SignalInvalidWithMessage(
ctx,
"Rounding was required");
}
FastInteger fastNewMantissa = FastInteger.FromBig(newmantissa);
if (
this.RoundGivenDigits(
lastDiscarded,
olderDiscarded,
rounding,
neg,
fastNewMantissa)) {
newmantissa = newmantissa.Add(EInteger.FromInt32(1));
}
}
} else {
IShiftAccumulator accum = this.helper.CreateShiftAccumulatorWithDigits(
mantissa,
lastDiscarded,
olderDiscarded);
accum.TruncateOrShiftRight(
shift,
false);
newmantissa = accum.getShiftedInt();
if (accum.getDiscardedDigitCount().signum() != 0 ||
(accum.getLastDiscardedDigit() | accum.getOlderDiscardedDigits()) !=
0) {
if (!mantissa.isZero()) {
flags |= EContext.FlagRounded;
}
if ((accum.getLastDiscardedDigit() | accum.getOlderDiscardedDigits()) != 0) {
flags |= EContext.FlagInexact | EContext.FlagRounded;
if (rounding == ERounding.None) {
return this.SignalInvalidWithMessage(
ctx,
"Rounding was required");
}
}
if (this.RoundGivenAccum(accum, rounding, neg)) {
newmantissa = newmantissa.Add(EInteger.FromInt32(1));
}
}
}
if (ctx != null && ctx.getHasFlags()) {
ctx.setFlags(ctx.getFlags()|(flags));
}
return this.helper.CreateNewWithFlags(
newmantissa,
desiredExponent,
neg ? BigNumberFlags.FlagNegative : 0);
}
private int[] RoundToScaleStatus(
EInteger remainder,
EInteger divisor,
EContext ctx) {
ERounding rounding = (ctx == null) ? ERounding.HalfEven : ctx.getRounding();
int lastDiscarded = 0;
int olderDiscarded = 0;
if (!remainder.isZero()) {
if (rounding == ERounding.HalfDown || rounding == ERounding.HalfUp ||
rounding == ERounding.HalfEven) {
int cmpHalf = CompareToHalf(remainder, divisor);
if (cmpHalf == 0) {
// remainder is exactly half
lastDiscarded = this.thisRadix / 2;
olderDiscarded = 0;
} else if (cmpHalf > 0) {
// remainder is greater than half
lastDiscarded = this.thisRadix / 2;
olderDiscarded = 1;
} else {
// remainder is less than half
lastDiscarded = 0;
olderDiscarded = 1;
}
} else {
// Rounding mode doesn't care about
// whether remainder is exactly half
if (rounding == ERounding.None) {
// Rounding was required
return null;
}
lastDiscarded = 1;
olderDiscarded = 1;
}
}
return new int[] { lastDiscarded, olderDiscarded };
}
private T SignalDivideByZero(EContext ctx, boolean neg) {
if (ctx != null && ctx.getHasFlags()) {
ctx.setFlags(ctx.getFlags()|(EContext.FlagDivideByZero));
}
if (this.support == BigNumberFlags.FiniteOnly) {
throw new ArithmeticException("Division by zero");
}
int flags = BigNumberFlags.FlagInfinity |
(neg ? BigNumberFlags.FlagNegative : 0);
return this.helper.CreateNewWithFlags(
EInteger.FromInt32(0),
EInteger.FromInt32(0),
flags);
}
private T SignalingNaNInvalid(T value, EContext ctx) {
if (ctx != null && ctx.getHasFlags()) {
ctx.setFlags(ctx.getFlags()|(EContext.FlagInvalid));
}
return this.ReturnQuietNaN(value, ctx);
}
private T SignalInvalid(EContext ctx) {
if (ctx != null && ctx.getHasFlags()) {
ctx.setFlags(ctx.getFlags()|(EContext.FlagInvalid));
}
if (this.support == BigNumberFlags.FiniteOnly) {
throw new ArithmeticException("Invalid operation");
}
return this.helper.CreateNewWithFlags(
EInteger.FromInt32(0),
EInteger.FromInt32(0),
BigNumberFlags.FlagQuietNaN);
}
private T SignalInvalidWithMessage(EContext ctx, String str) {
if (ctx != null && ctx.getHasFlags()) {
ctx.setFlags(ctx.getFlags()|(EContext.FlagInvalid));
}
if (this.support == BigNumberFlags.FiniteOnly) {
throw new ArithmeticException(str);
}
// if (str.indexOf("Rounding was") < 0) {
// throw new ArithmeticException(str);
// } else {
// System.out.println(str);
// }
return this.helper.CreateNewWithFlags(
EInteger.FromInt32(0),
EInteger.FromInt32(0),
BigNumberFlags.FlagQuietNaN);
}
public T SignalOverflow(EContext ctx, boolean neg) {
if (ctx != null) {
ERounding roundingOnOverflow = ctx.getRounding();
if (ctx.getHasFlags()) {
ctx.setFlags(ctx.getFlags()|(EContext.FlagOverflow |
EContext.FlagInexact | EContext.FlagRounded));
}
if (roundingOnOverflow == ERounding.None) {
return this.SignalInvalidWithMessage(
ctx,
"Rounding was required");
}
if (ctx.getHasMaxPrecision() && ctx.getHasExponentRange() &&
(roundingOnOverflow == ERounding.Down ||
roundingOnOverflow == ERounding.ZeroFiveUp ||
roundingOnOverflow == ERounding.OddOrZeroFiveUp ||
roundingOnOverflow == ERounding.Odd ||
(roundingOnOverflow == ERounding.Ceiling && neg) ||
(roundingOnOverflow == ERounding.Floor && !neg))) {
// Set to the highest possible value for
// the given precision
EInteger overflowMant = EInteger.FromInt32(0);
FastInteger fastPrecision = FastInteger.FromBig(ctx.getPrecision());
overflowMant = this.TryMultiplyByRadixPower(
EInteger.FromInt32(1),
fastPrecision);
if (overflowMant == null) {
return this.SignalInvalidWithMessage(
ctx,
"Result requires too much memory");
}
overflowMant = overflowMant.Subtract(EInteger.FromInt32(1));
FastInteger clamp = FastInteger.FromBig(ctx.getEMax());
if (ctx.getAdjustExponent()) {
clamp.Increment().Subtract(fastPrecision);
}
return this.helper.CreateNewWithFlags(
overflowMant,
clamp.ToEInteger(),
neg ? BigNumberFlags.FlagNegative : 0);
}
}
return this.support == BigNumberFlags.FiniteOnly ? null :
this.helper.CreateNewWithFlags(
EInteger.FromInt32(0),
EInteger.FromInt32(0),
(neg ? BigNumberFlags.FlagNegative : 0) | BigNumberFlags.FlagInfinity);
}
private T SquareRootHandleSpecial(T thisValue, EContext ctx) {
int thisFlags = this.helper.GetFlags(thisValue);
if ((thisFlags & BigNumberFlags.FlagSpecial) != 0) {
if ((thisFlags & BigNumberFlags.FlagSignalingNaN) != 0) {
return this.SignalingNaNInvalid(thisValue, ctx);
}
if ((thisFlags & BigNumberFlags.FlagQuietNaN) != 0) {
return this.ReturnQuietNaN(thisValue, ctx);
}
if ((thisFlags & BigNumberFlags.FlagInfinity) != 0) {
// Square root of infinity
return ((thisFlags & BigNumberFlags.FlagNegative) != 0) ?
this.SignalInvalid(ctx) : thisValue;
}
}
int sign = this.helper.GetSign(thisValue);
return (sign < 0) ? this.SignalInvalid(ctx) : null;
}
private EInteger TryMultiplyByRadixPower(
EInteger bi,
int radixPowerInt) {
if (bi.isZero() || radixPowerInt == 0) {
return bi;
}
return this.helper.MultiplyByRadixPower(bi,
new FastInteger(radixPowerInt));
}
private EInteger TryMultiplyByRadixPower(
EInteger bi,
FastInteger radixPower) {
if (bi.isZero()) {
return bi;
}
if (!radixPower.CanFitInInt32()) {
// NOTE: For radix 10, each digit fits less than 1 byte; the
// supported byte length is thus less than the maximum value
// of a 32-bit integer (2GB).
FastInteger fastBI = FastInteger.FromBig(valueMaxDigits);
if (this.thisRadix != 10 || radixPower.compareTo(fastBI) > 0) {
return null;
}
}
return this.helper.MultiplyByRadixPower(bi, radixPower);
}
private FastIntegerFixed TryMultiplyByRadixPowerFastInt(
FastIntegerFixed bi,
FastIntegerFixed radixPower) {
if (bi.isValueZero()) {
return bi;
}
if (!radixPower.CanFitInInt32()) {
// NOTE: For radix 10, each digit fits less than 1 byte; the
// supported byte length is thus less than the maximum value
// of a 32-bit integer (2GB).
FastIntegerFixed fastBI = FastIntegerFixed.FromBig(valueMaxDigits);
if (this.thisRadix != 10 || radixPower.compareTo(fastBI) > 0) {
return null;
}
return FastIntegerFixed.FromBig(this.helper.MultiplyByRadixPower(
bi.ToEInteger(),
FastInteger.FromBig(radixPower.ToEInteger())));
} else {
return FastIntegerFixed.FromBig(this.helper.MultiplyByRadixPower(
bi.ToEInteger(),
new FastInteger(radixPower.ToInt32())));
}
}
private FastIntegerFixed TryMultiplyByRadixPowerFastInt(
FastIntegerFixed bi,
FastInteger radixPower) {
if (bi.isValueZero()) {
return bi;
}
if (!radixPower.CanFitInInt32()) {
// NOTE: For radix 10, each digit fits less than 1 byte; the
// supported byte length is thus less than the maximum value
// of a 32-bit integer (2GB).
FastInteger fastBI = FastInteger.FromBig(valueMaxDigits);
if (this.thisRadix != 10 || radixPower.compareTo(fastBI) > 0) {
return null;
}
}
return FastIntegerFixed.FromBig(this.helper.MultiplyByRadixPower(
bi.ToEInteger(),
radixPower));
}
private T ValueOf(int value, EContext ctx) {
return (ctx == null || !ctx.getHasExponentRange() ||
ctx.ExponentWithinRange(EInteger.FromInt32(0))) ?
this.helper.ValueOf(value) :
this.RoundToPrecision(this.helper.ValueOf(value), ctx);
}
}