Skip to content

Commit 2a79399

Browse files
committed
Add Odd and OddOrZeroFiveUp rounding modes
1 parent 52566c9 commit 2a79399

File tree

7 files changed

+204
-87
lines changed

7 files changed

+204
-87
lines changed

src/main/java/com/upokecenter/util/RadixMath.java

Lines changed: 45 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,8 @@ private T SignalOverflow2(PrecisionContext ctx, boolean neg) {
410410
if (ctx.getHasMaxPrecision() && ctx.getHasExponentRange() &&
411411
(roundingOnOverflow == Rounding.Down || roundingOnOverflow ==
412412
Rounding.ZeroFiveUp ||
413+
(roundingOnOverflow == Rounding.OddOrZeroFiveUp ||
414+
roundingOnOverflow == Rounding.Odd) ||
413415
(roundingOnOverflow == Rounding.Ceiling && neg) ||
414416
(roundingOnOverflow == Rounding.Floor && !neg))) {
415417
// Set to the highest possible value for
@@ -467,7 +469,8 @@ private boolean Round(
467469
incremented |= !fastint.isEvenNumber();
468470
}
469471
}
470-
} else if (rounding == Rounding.ZeroFiveUp) {
472+
} else if (rounding == Rounding.ZeroFiveUp ||
473+
(rounding == Rounding.OddOrZeroFiveUp && this.thisRadix != 2)) {
471474
int radix = this.thisRadix;
472475
if ((accum.getLastDiscardedDigit() | accum.getOlderDiscardedDigits()) != 0) {
473476
if (radix == 2) {
@@ -499,6 +502,9 @@ private boolean RoundGivenDigits(
499502
BigInteger bigval) {
500503
boolean incremented = false;
501504
int radix = this.thisRadix;
505+
if (rounding == Rounding.OddOrZeroFiveUp) {
506+
rounding = (radix == 2) ? Rounding.Odd : Rounding.ZeroFiveUp;
507+
}
502508
if (rounding == Rounding.HalfUp) {
503509
incremented |= lastDiscarded >= (radix / 2);
504510
} else if (rounding == Rounding.HalfEven) {
@@ -523,6 +529,8 @@ private boolean RoundGivenDigits(
523529
(radix / 2) && olderDiscarded != 0);
524530
} else if (rounding == Rounding.Up) {
525531
incremented |= (lastDiscarded | olderDiscarded) != 0;
532+
} else if (rounding == Rounding.Odd) {
533+
incremented |= (lastDiscarded | olderDiscarded) != 0 && bigval.testBit(0) == false;
526534
} else if (rounding == Rounding.ZeroFiveUp) {
527535
if ((lastDiscarded | olderDiscarded) != 0) {
528536
if (radix == 2) {
@@ -725,23 +733,30 @@ public T Negate(T value, PrecisionContext ctx) {
725733
return this.ReturnQuietNaN(value, ctx);
726734
}
727735
BigInteger mant = this.helper.GetMantissa(value);
736+
T zero;
728737
if ((flags & BigNumberFlags.FlagInfinity) == 0 && mant.signum() == 0) {
729738
if ((flags & BigNumberFlags.FlagNegative) == 0) {
730739
// positive 0 minus positive 0 is always positive 0
731-
return this.RoundToPrecision(
732-
this.helper.CreateNewWithFlags(mant, this.helper.GetExponent(value), flags & ~BigNumberFlags.FlagNegative),
733-
ctx);
740+
zero = this.helper.CreateNewWithFlags(
741+
mant,
742+
this.helper.GetExponent(value),
743+
flags & ~BigNumberFlags.FlagNegative);
744+
return this.RoundToPrecision(zero, ctx);
734745
}
735746
if (ctx != null && ctx.getRounding() == Rounding.Floor) {
736747
// positive 0 minus negative 0 is negative 0 only if
737748
// the rounding is Floor
738-
return this.RoundToPrecision(
739-
this.helper.CreateNewWithFlags(mant, this.helper.GetExponent(value), flags | BigNumberFlags.FlagNegative),
740-
ctx);
749+
zero = this.helper.CreateNewWithFlags(
750+
mant,
751+
this.helper.GetExponent(value),
752+
flags | BigNumberFlags.FlagNegative);
753+
} else {
754+
zero = this.helper.CreateNewWithFlags(
755+
mant,
756+
this.helper.GetExponent(value),
757+
flags & ~BigNumberFlags.FlagNegative);
741758
}
742-
return this.RoundToPrecision(
743-
this.helper.CreateNewWithFlags(mant, this.helper.GetExponent(value), flags & ~BigNumberFlags.FlagNegative),
744-
ctx);
759+
return this.RoundToPrecision(zero, ctx);
745760
}
746761
flags ^= BigNumberFlags.FlagNegative;
747762
return this.RoundToPrecision(
@@ -863,8 +878,8 @@ public T Pi(PrecisionContext ctx) {
863878
T a = this.helper.ValueOf(1);
864879
PrecisionContext ctxdiv = SetPrecisionIfLimited(
865880
ctx,
866-
ctx.getPrecision().add(BigInteger.TEN)).WithRounding(this.thisRadix == 2 ?
867-
Rounding.HalfEven : Rounding.ZeroFiveUp);
881+
ctx.getPrecision().add(BigInteger.TEN))
882+
.WithRounding(Rounding.OddOrZeroFiveUp);
868883
T two = this.helper.ValueOf(2);
869884
T b = this.Divide(a, this.SquareRoot(two, ctxdiv), ctxdiv);
870885
T four = this.helper.ValueOf(4);
@@ -930,8 +945,7 @@ private T LnInternal(
930945
PrecisionContext ctxdiv = SetPrecisionIfLimited(
931946
ctx,
932947
workingPrecision.add(BigInteger.valueOf(6)))
933-
.WithRounding(this.thisRadix == 2 ? Rounding.HalfEven :
934-
Rounding.ZeroFiveUp);
948+
.WithRounding(Rounding.OddOrZeroFiveUp);
935949
T z = this.Add(this.NegateRaw(thisValue), this.helper.ValueOf(1), null);
936950
T zpow = this.Multiply(z, z, ctxdiv);
937951
T guess = this.NegateRaw(z);
@@ -973,8 +987,7 @@ private T ExpInternal(
973987
PrecisionContext ctxdiv = SetPrecisionIfLimited(
974988
ctx,
975989
workingPrecision.add(BigInteger.valueOf(6)))
976-
.WithRounding(this.thisRadix == 2 ? Rounding.Down :
977-
Rounding.ZeroFiveUp);
990+
.WithRounding(Rounding.OddOrZeroFiveUp);
978991
BigInteger bigintN = BigInteger.valueOf(2);
979992
BigInteger facto = BigInteger.ONE;
980993
// Guess starts with 1 + thisValue
@@ -1059,8 +1072,7 @@ private T PowerIntegral(
10591072
PrecisionContext ctxdiv = SetPrecisionIfLimited(
10601073
ctx,
10611074
ctx.getPrecision().add(bigError))
1062-
.WithRounding(this.thisRadix == 2 ? Rounding.HalfEven :
1063-
Rounding.ZeroFiveUp).WithBlankFlags();
1075+
.WithRounding(Rounding.OddOrZeroFiveUp).WithBlankFlags();
10641076
if (sign < 0) {
10651077
// Use the reciprocal for negative powers
10661078
thisValue = this.Divide(one, thisValue, ctxdiv);
@@ -1332,8 +1344,7 @@ public T Power(T thisValue, T pow, PrecisionContext ctx) {
13321344
PrecisionContext ctxdiv = SetPrecisionIfLimited(
13331345
ctx,
13341346
ctx.getPrecision().add(guardDigits));
1335-
ctxdiv = ctxdiv.WithRounding(this.thisRadix == 2 ? Rounding.HalfEven :
1336-
Rounding.ZeroFiveUp).WithBlankFlags();
1347+
ctxdiv = ctxdiv.WithRounding(Rounding.OddOrZeroFiveUp).WithBlankFlags();
13371348
T lnresult = this.Ln(thisValue, ctxdiv);
13381349
/* System.out.println("guard= " + guardDigits + " prec=" + ctx.getPrecision()+
13391350
" newprec= " + ctxdiv.getPrecision());
@@ -1444,8 +1455,7 @@ public T Log10(T thisValue, PrecisionContext ctx) {
14441455
PrecisionContext ctxdiv = SetPrecisionIfLimited(
14451456
ctx,
14461457
ctx.getPrecision().add(BigInteger.TEN))
1447-
.WithRounding(this.thisRadix == 2 ? Rounding.HalfEven :
1448-
Rounding.ZeroFiveUp).WithBlankFlags();
1458+
.WithRounding(Rounding.OddOrZeroFiveUp).WithBlankFlags();
14491459
T logNatural = this.Ln(thisValue, ctxdiv);
14501460
T logTen = this.LnTenConstant(ctxdiv);
14511461
// T logTen = this.Ln(this.helper.ValueOf(10), ctxdiv);
@@ -1499,8 +1509,7 @@ private T LnTenConstant(PrecisionContext ctx) {
14991509
PrecisionContext ctxdiv = SetPrecisionIfLimited(
15001510
ctx,
15011511
ctx.getPrecision().add(bigError))
1502-
.WithRounding(this.thisRadix == 2 ? Rounding.HalfEven :
1503-
Rounding.ZeroFiveUp).WithBlankFlags();
1512+
.WithRounding(Rounding.OddOrZeroFiveUp).WithBlankFlags();
15041513
for (int i = 0; i < 9; ++i) {
15051514
thisValue = this.SquareRoot(thisValue, ctxdiv.WithUnlimitedExponents());
15061515
}
@@ -1563,8 +1572,7 @@ public T Ln(T thisValue, PrecisionContext ctx) {
15631572
FastInteger error = new FastInteger(10);
15641573
BigInteger bigError = error.AsBigInteger();
15651574
ctxdiv = SetPrecisionIfLimited(ctx, ctx.getPrecision().add(bigError))
1566-
.WithRounding(this.thisRadix == 2 ? Rounding.HalfEven :
1567-
Rounding.ZeroFiveUp).WithBlankFlags();
1575+
.WithRounding(Rounding.OddOrZeroFiveUp).WithBlankFlags();
15681576
T quarter = this.Divide(one, this.helper.ValueOf(4), ctxCopy);
15691577
if (this.compareTo(thisValue, quarter) <= 0) {
15701578
// One quarter or less
@@ -1620,8 +1628,7 @@ public T Ln(T thisValue, PrecisionContext ctx) {
16201628
error = new FastInteger(10);
16211629
bigError = error.AsBigInteger();
16221630
ctxdiv = SetPrecisionIfLimited(ctx, ctx.getPrecision().add(bigError))
1623-
.WithRounding(this.thisRadix == 2 ? Rounding.HalfEven :
1624-
Rounding.ZeroFiveUp).WithBlankFlags();
1631+
.WithRounding(Rounding.OddOrZeroFiveUp).WithBlankFlags();
16251632
T smallfrac = this.Divide(one, this.helper.ValueOf(10), ctxdiv);
16261633
T closeToOne = this.Add(one, smallfrac, null);
16271634
// Take square root until this value
@@ -1649,8 +1656,7 @@ public T Ln(T thisValue, PrecisionContext ctx) {
16491656
error = new FastInteger(10);
16501657
bigError = error.AsBigInteger();
16511658
ctxdiv = SetPrecisionIfLimited(ctx, ctx.getPrecision().add(bigError))
1652-
.WithRounding(this.thisRadix == 2 ? Rounding.HalfEven :
1653-
Rounding.ZeroFiveUp).WithBlankFlags();
1659+
.WithRounding(Rounding.OddOrZeroFiveUp).WithBlankFlags();
16541660
T smallfrac = this.Divide(one, this.helper.ValueOf(16), ctxdiv);
16551661
T closeToOne = this.Add(one, smallfrac, null);
16561662
if (this.compareTo(thisValue, closeToOne) >= 0) {
@@ -1729,8 +1735,7 @@ public T Exp(T thisValue, PrecisionContext ctx) {
17291735
PrecisionContext ctxdiv = SetPrecisionIfLimited(
17301736
ctx,
17311737
ctx.getPrecision().add(guardDigits))
1732-
.WithRounding(this.thisRadix == 2 ? Rounding.HalfEven :
1733-
Rounding.ZeroFiveUp).WithBlankFlags();
1738+
.WithRounding(Rounding.OddOrZeroFiveUp).WithBlankFlags();
17341739
if (sign == 0) {
17351740
thisValue = this.RoundToPrecision(one, ctxCopy);
17361741
} else if (sign > 0 && this.compareTo(thisValue, one) < 0) {
@@ -3550,8 +3555,10 @@ private T RoundToPrecisionInternal(
35503555
return this.SignalInvalidWithMessage(ctx, "Rounding was required");
35513556
}
35523557
if (!unlimitedPrec && (rounding == Rounding.Down || rounding ==
3553-
Rounding.ZeroFiveUp || (rounding == Rounding.Ceiling &&
3554-
neg) || (rounding == Rounding.Floor && !neg))) {
3558+
Rounding.ZeroFiveUp ||
3559+
(rounding == Rounding.OddOrZeroFiveUp && this.thisRadix != 2) ||
3560+
(rounding == Rounding.Ceiling && neg) || (rounding ==
3561+
Rounding.Floor && !neg))) {
35553562
// Set to the highest possible value for
35563563
// the given precision
35573564
BigInteger overflowMant = BigInteger.ZERO;
@@ -3758,7 +3765,10 @@ private T RoundToPrecisionInternal(
37583765
flags |= PrecisionContext.FlagOverflow |
37593766
PrecisionContext.FlagInexact | PrecisionContext.FlagRounded;
37603767
if (!unlimitedPrec && (rounding == Rounding.Down || rounding ==
3761-
Rounding.ZeroFiveUp || (rounding == Rounding.Ceiling &&
3768+
Rounding.ZeroFiveUp ||
3769+
(rounding == Rounding.OddOrZeroFiveUp || rounding == Rounding.Odd) ||
3770+
3771+
(rounding == Rounding.Ceiling &&
37623772
neg) || (rounding == Rounding.Floor && !neg))) {
37633773
// Set to the highest possible value for
37643774
// the given precision

src/main/java/com/upokecenter/util/Rounding.java

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,22 @@ public enum Rounding {
6767
* If there is a fractional part and if the last digit before rounding is 0 or
6868
* half the radix, the number is rounded to the closest representable
6969
* number away from zero; otherwise the fractional part is discarded. In
70-
* overflow, the fractional part is always discarded. This rounding mode
71-
* is useful for rounding intermediate results at a slightly higher
72-
* precision than the final precision.
70+
* overflow, the fractional part is always discarded.
7371
*/
74-
ZeroFiveUp
72+
ZeroFiveUp,
73+
74+
/**
75+
* If there is a fractional part and the whole number part is even, the number
76+
* is rounded to the closest representable odd number away from zero.
77+
*/
78+
Odd,
79+
80+
/**
81+
* For binary floating point numbers, this is the same as Odd. For other bases
82+
* (including decimal numbers), this is the same as ZeroFiveUp. This
83+
* rounding mode is useful for rounding intermediate results at a
84+
* slightly higher precision (at least 2 bits more for binary) than the
85+
* final precision.
86+
*/
87+
OddOrZeroFiveUp
7588
}

src/main/java/com/upokecenter/util/SimpleRadixMath.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,8 @@ private T SignalOverflow2(PrecisionContext pc, boolean neg) {
380380
if (pc.getHasMaxPrecision() && pc.getHasExponentRange() &&
381381
(roundingOnOverflow == Rounding.Down || roundingOnOverflow ==
382382
Rounding.ZeroFiveUp ||
383+
(roundingOnOverflow == Rounding.OddOrZeroFiveUp) ||
384+
(roundingOnOverflow == Rounding.Odd) ||
383385
(roundingOnOverflow == Rounding.Ceiling && neg) ||
384386
(roundingOnOverflow == Rounding.Floor && !neg))) {
385387
// Set to the highest possible value for

0 commit comments

Comments
 (0)