public virtual Number Calculate(BigDecimal num) { if (num == null) { return -0; } return num.Negate(); }
public static BigDecimal /*!*/ Negate(RubyContext /*!*/ context, BigDecimal /*!*/ self) { return(BigDecimal.Negate(GetConfig(context), self)); }
public static BigDecimal Exp(BigDecimal x) { /* To calculate the value if x is negative, use exp(-x) = 1/exp(x) */ if (x.CompareTo(BigDecimal.Zero) < 0) { BigDecimal invx = Exp(x.Negate()); /* Relative error in inverse of invx is the same as the relative errror in invx. * This is used to define the precision of the result. */ var mc = new MathContext(invx.Precision); return BigDecimal.One.Divide(invx, mc); } if (x.CompareTo(BigDecimal.Zero) == 0) { /* recover the valid number of digits from x.ulp(), if x hits the * zero. The x.precision() is 1 then, and does not provide this information. */ return ScalePrecision(BigDecimal.One, -(int) (System.Math.Log10(x.Ulp().ToDouble()))); } /* Push the number in the Taylor expansion down to a small * value where TAYLOR_NTERM terms will do. If x<1, the n-th term is of the order * x^n/n!, and equal to both the absolute and relative error of the result * since the result is close to 1. The x.ulp() sets the relative and absolute error * of the result, as estimated from the first Taylor term. * We want x^TAYLOR_NTERM/TAYLOR_NTERM! < x.ulp, which is guaranteed if * x^TAYLOR_NTERM < TAYLOR_NTERM*(TAYLOR_NTERM-1)*...*x.ulp. */ double xDbl = x.ToDouble(); double xUlpDbl = x.Ulp().ToDouble(); if (System.Math.Pow(xDbl, TaylorNterm) < TaylorNterm*(TaylorNterm - 1.0)*(TaylorNterm - 2.0)*xUlpDbl) { /* Add TAYLOR_NTERM terms of the Taylor expansion (Euler's sum formula) */ BigDecimal resul = BigDecimal.One; /* x^i */ BigDecimal xpowi = BigDecimal.One; /* i factorial */ BigInteger ifac = BigInteger.One; /* TAYLOR_NTERM terms to be added means we move x.ulp() to the right * for each power of 10 in TAYLOR_NTERM, so the addition won't add noise beyond * what's already in x. */ var mcTay = new MathContext(ErrorToPrecision(1d, xUlpDbl/TaylorNterm)); for (int i = 1; i <= TaylorNterm; i++) { ifac = ifac.Multiply(BigInteger.ValueOf(i)); xpowi = xpowi.Multiply(x); BigDecimal c = xpowi.Divide(new BigDecimal(ifac), mcTay); resul = resul.Add(c); if (System.Math.Abs(xpowi.ToDouble()) < i && System.Math.Abs(c.ToDouble()) < 0.5*xUlpDbl) break; } /* exp(x+deltax) = exp(x)(1+deltax) if deltax is <<1. So the relative error * in the result equals the absolute error in the argument. */ var mc = new MathContext(ErrorToPrecision(xUlpDbl/2d)); return resul.Round(mc); } else { /* Compute exp(x) = (exp(0.1*x))^10. Division by 10 does not lead * to loss of accuracy. */ var exSc = (int) (1.0 - System.Math.Log10(TaylorNterm*(TaylorNterm - 1.0)*(TaylorNterm - 2.0)*xUlpDbl /System.Math.Pow(xDbl, TaylorNterm))/(TaylorNterm - 1.0)); BigDecimal xby10 = x.ScaleByPowerOfTen(-exSc); BigDecimal expxby10 = Exp(xby10); /* Final powering by 10 means that the relative error of the result * is 10 times the relative error of the base (First order binomial expansion). * This looses one digit. */ var mc = new MathContext(expxby10.Precision - exSc); /* Rescaling the powers of 10 is done in chunks of a maximum of 8 to avoid an invalid operation * response by the BigDecimal.pow library or integer overflow. */ while (exSc > 0) { int exsub = System.Math.Min(8, exSc); exSc -= exsub; var mctmp = new MathContext(expxby10.Precision - exsub + 2); int pex = 1; while (exsub-- > 0) pex *= 10; expxby10 = expxby10.Pow(pex, mctmp); } return expxby10.Round(mc); } }
public static BigDecimal Cos(BigDecimal x) { if (x.CompareTo(BigDecimal.Zero) < 0) return Cos(x.Negate()); if (x.CompareTo(BigDecimal.Zero) == 0) return BigDecimal.One; /* reduce modulo 2pi */ BigDecimal res = Mod2Pi(x); double errpi = 0.5*System.Math.Abs(x.Ulp().ToDouble()); var mc = new MathContext(2 + ErrorToPrecision(3.14159, errpi)); BigDecimal p = PiRound(mc); mc = new MathContext(x.Precision); if (res.CompareTo(p) > 0) { /* pi<x<=2pi: cos(x)= - cos(x-pi) */ return Cos(SubtractRound(res, p)).Negate(); } if (res.Multiply(BigDecimal.ValueOf(2)).CompareTo(p) > 0) { /* pi/2<x<=pi: cos(x)= -cos(pi-x) */ return Cos(SubtractRound(p, res)).Negate(); } /* for the range 0<=x<Pi/2 one could use cos(2x)= 1-2*sin^2(x) * to split this further, or use the cos up to pi/4 and the sine higher up. throw new ProviderException("Not implemented: cosine ") ; */ if (res.Multiply(BigDecimal.ValueOf(4)).CompareTo(p) > 0) { /* x>pi/4: cos(x) = sin(pi/2-x) */ return Sin(SubtractRound(p.Divide(BigDecimal.ValueOf(2)), res)); } /* Simple Taylor expansion, sum_{i=0..infinity} (-1)^(..)res^(2i)/(2i)! */ BigDecimal resul = BigDecimal.One; /* x^i */ BigDecimal xpowi = BigDecimal.One; /* 2i factorial */ BigInteger ifac = BigInteger.One; /* The absolute error in the result is the error in x^2/2 which is x times the error in x. */ double xUlpDbl = 0.5*res.Ulp().ToDouble()*res.ToDouble(); /* The error in the result is set by the error in x^2/2 itself, xUlpDbl. * We need at most k terms to push x^(2k+1)/(2k+1)! below this value. * x^(2k) < xUlpDbl; (2k)*log(x) < log(xUlpDbl); */ int k = (int) (System.Math.Log(xUlpDbl)/System.Math.Log(res.ToDouble()))/2; var mcTay = new MathContext(ErrorToPrecision(1d, xUlpDbl/k)); for (int i = 1;; i++) { /* TBD: at which precision will 2*i-1 or 2*i overflow? */ ifac = ifac.Multiply(BigInteger.ValueOf((2*i - 1))); ifac = ifac.Multiply(BigInteger.ValueOf((2*i))); xpowi = xpowi.Multiply(res).Multiply(res).Negate(); BigDecimal corr = xpowi.Divide(new BigDecimal(ifac), mcTay); resul = resul.Add(corr); if (corr.Abs().ToDouble() < 0.5*xUlpDbl) break; } /* The error in the result is governed by the error in x itself. */ mc = new MathContext(ErrorToPrecision(resul.ToDouble(), xUlpDbl)); return resul.Round(mc); }
public static BigDecimal Cot(BigDecimal x) { if (x.CompareTo(BigDecimal.Zero) == 0) { throw new ArithmeticException("Cannot take cot of zero " + x); } if (x.CompareTo(BigDecimal.Zero) < 0) { return Cot(x.Negate()).Negate(); } /* reduce modulo pi */ BigDecimal res = ModPi(x); /* absolute error in the result is err(x)/sin^2(x) to lowest order */ double xDbl = res.ToDouble(); double xUlpDbl = x.Ulp().ToDouble()/2d; double eps = xUlpDbl/2d/System.Math.Pow(System.Math.Sin(xDbl), 2d); BigDecimal xhighpr = ScalePrecision(res, 2); BigDecimal xhighprSq = MultiplyRound(xhighpr, xhighpr); var mc = new MathContext(ErrorToPrecision(xhighpr.ToDouble(), eps)); BigDecimal resul = BigDecimal.One.Divide(xhighpr, mc); /* x^(2i-1) */ BigDecimal xpowi = xhighpr; var b = new Bernoulli(); /* 2^(2i) */ var fourn = BigInteger.Parse("4"); /* (2i)! */ BigInteger fac = BigInteger.One; for (int i = 1;; i++) { Rational f = b[2*i]; fac = fac.Multiply(BigInteger.ValueOf((2*i))).Multiply(BigInteger.ValueOf((2*i - 1))); f = f.Multiply(fourn).Divide(fac); BigDecimal c = MultiplyRound(xpowi, f); if (i%2 == 0) resul = resul.Add(c); else resul = resul.Subtract(c); if (System.Math.Abs(c.ToDouble()) < 0.1*eps) break; fourn = fourn.ShiftLeft(2); xpowi = MultiplyRound(xpowi, xhighprSq); } mc = new MathContext(ErrorToPrecision(resul.ToDouble(), eps)); return resul.Round(mc); }
public static BigDecimal Asin(BigDecimal x) { if (x.CompareTo(BigDecimal.One) > 0 || x.CompareTo(BigDecimal.One.Negate()) < 0) { throw new ArithmeticException("Out of range argument " + x + " of asin"); } if (x.CompareTo(BigDecimal.Zero) == 0) return BigDecimal.Zero; if (x.CompareTo(BigDecimal.One) == 0) { /* arcsin(1) = pi/2 */ double errpi = System.Math.Sqrt(x.Ulp().ToDouble()); var mc = new MathContext(ErrorToPrecision(3.14159, errpi)); return PiRound(mc).Divide(new BigDecimal(2)); } if (x.CompareTo(BigDecimal.Zero) < 0) { return Asin(x.Negate()).Negate(); } if (x.ToDouble() > 0.7) { BigDecimal xCompl = BigDecimal.One.Subtract(x); double xDbl = x.ToDouble(); double xUlpDbl = x.Ulp().ToDouble()/2d; double eps = xUlpDbl/2d/System.Math.Sqrt(1d - System.Math.Pow(xDbl, 2d)); BigDecimal xhighpr = ScalePrecision(xCompl, 3); BigDecimal xhighprV = DivideRound(xhighpr, 4); BigDecimal resul = BigDecimal.One; /* x^(2i+1) */ BigDecimal xpowi = BigDecimal.One; /* i factorial */ BigInteger ifacN = BigInteger.One; BigInteger ifacD = BigInteger.One; for (int i = 1;; i++) { ifacN = ifacN.Multiply(BigInteger.ValueOf((2*i - 1))); ifacD = ifacD.Multiply(BigInteger.ValueOf(i)); if (i == 1) xpowi = xhighprV; else xpowi = MultiplyRound(xpowi, xhighprV); BigDecimal c = DivideRound(MultiplyRound(xpowi, ifacN), ifacD.Multiply(BigInteger.ValueOf((2*i + 1)))); resul = resul.Add(c); /* series started 1+x/12+... which yields an estimate of the sum's error */ if (System.Math.Abs(c.ToDouble()) < xUlpDbl/120d) break; } /* sqrt(2*z)*(1+...) */ xpowi = Sqrt(xhighpr.Multiply(new BigDecimal(2))); resul = MultiplyRound(xpowi, resul); var mc = new MathContext(resul.Precision); BigDecimal pihalf = PiRound(mc).Divide(new BigDecimal(2)); mc = new MathContext(ErrorToPrecision(resul.ToDouble(), eps)); return pihalf.Subtract(resul, mc); } else { /* absolute error in the result is err(x)/sqrt(1-x^2) to lowest order */ double xDbl = x.ToDouble(); double xUlpDbl = x.Ulp().ToDouble()/2d; double eps = xUlpDbl/2d/System.Math.Sqrt(1d - System.Math.Pow(xDbl, 2d)); BigDecimal xhighpr = ScalePrecision(x, 2); BigDecimal xhighprSq = MultiplyRound(xhighpr, xhighpr); BigDecimal resul = xhighpr.Plus(); /* x^(2i+1) */ BigDecimal xpowi = xhighpr; /* i factorial */ BigInteger ifacN = BigInteger.One; BigInteger ifacD = BigInteger.One; for (int i = 1;; i++) { ifacN = ifacN.Multiply(BigInteger.ValueOf((2*i - 1))); ifacD = ifacD.Multiply(BigInteger.ValueOf((2*i))); xpowi = MultiplyRound(xpowi, xhighprSq); BigDecimal c = DivideRound(MultiplyRound(xpowi, ifacN), ifacD.Multiply(BigInteger.ValueOf((2*i + 1)))); resul = resul.Add(c); if (System.Math.Abs(c.ToDouble()) < 0.1*eps) break; } var mc = new MathContext(ErrorToPrecision(resul.ToDouble(), eps)); return resul.Round(mc); } }
public static BigDecimal Cbrt(BigDecimal x) { if (x.CompareTo(BigDecimal.Zero) < 0) return Root(3, x.Negate()).Negate(); return Root(3, x); }
public static BigDecimal Tan(BigDecimal x) { if (x.CompareTo(BigDecimal.Zero) == 0) return BigDecimal.Zero; if (x.CompareTo(BigDecimal.Zero) < 0) { return Tan(x.Negate()).Negate(); } /* reduce modulo pi */ BigDecimal res = ModPi(x); /* absolute error in the result is err(x)/cos^2(x) to lowest order */ double xDbl = res.ToDouble(); double xUlpDbl = x.Ulp().ToDouble()/2d; double eps = xUlpDbl/2d/System.Math.Pow(System.Math.Cos(xDbl), 2d); if (xDbl > 0.8) { /* tan(x) = 1/cot(x) */ BigDecimal co = Cot(x); var mc = new MathContext(ErrorToPrecision(1d/co.ToDouble(), eps)); return BigDecimal.One.Divide(co, mc); } else { BigDecimal xhighpr = ScalePrecision(res, 2); BigDecimal xhighprSq = MultiplyRound(xhighpr, xhighpr); BigDecimal resul = xhighpr.Plus(); /* x^(2i+1) */ BigDecimal xpowi = xhighpr; var b = new Bernoulli(); /* 2^(2i) */ BigInteger fourn = BigInteger.ValueOf(4); /* (2i)! */ BigInteger fac = BigInteger.ValueOf(2); for (int i = 2;; i++) { Rational f = b[2*i].Abs(); fourn = fourn.ShiftLeft(2); fac = fac.Multiply(BigInteger.ValueOf((2*i))).Multiply(BigInteger.ValueOf((2*i - 1))); f = f.Multiply(fourn).Multiply(fourn.Subtract(BigInteger.One)).Divide(fac); xpowi = MultiplyRound(xpowi, xhighprSq); BigDecimal c = MultiplyRound(xpowi, f); resul = resul.Add(c); if (System.Math.Abs(c.ToDouble()) < 0.1*eps) break; } var mc = new MathContext(ErrorToPrecision(resul.ToDouble(), eps)); return resul.Round(mc); } }
public static BigDecimal Sin(BigDecimal x) { if (x.CompareTo(BigDecimal.Zero) < 0) return Sin(x.Negate()).Negate(); if (x.CompareTo(BigDecimal.Zero) == 0) return BigDecimal.Zero; /* reduce modulo 2pi */ BigDecimal res = Mod2Pi(x); double errpi = 0.5*System.Math.Abs(x.Ulp().ToDouble()); var mc = new MathContext(2 + ErrorToPrecision(3.14159, errpi)); BigDecimal p = PiRound(mc); mc = new MathContext(x.Precision); if (res.CompareTo(p) > 0) { /* pi<x<=2pi: sin(x)= - sin(x-pi) */ return Sin(SubtractRound(res, p)).Negate(); } if (res.Multiply(BigDecimal.ValueOf(2)).CompareTo(p) > 0) { /* pi/2<x<=pi: sin(x)= sin(pi-x) */ return Sin(SubtractRound(p, res)); } /* for the range 0<=x<Pi/2 one could use sin(2x)=2sin(x)cos(x) * to split this further. Here, use the sine up to pi/4 and the cosine higher up. */ if (res.Multiply(BigDecimal.ValueOf(4)).CompareTo(p) > 0) { /* x>pi/4: sin(x) = cos(pi/2-x) */ return Cos(SubtractRound(p.Divide(BigDecimal.ValueOf(2)), res)); } /* Simple Taylor expansion, sum_{i=1..infinity} (-1)^(..)res^(2i+1)/(2i+1)! */ BigDecimal resul = res; /* x^i */ BigDecimal xpowi = res; /* 2i+1 factorial */ BigInteger ifac = BigInteger.One; /* The error in the result is set by the error in x itself. */ double xUlpDbl = res.Ulp().ToDouble(); /* The error in the result is set by the error in x itself. * We need at most k terms to squeeze x^(2k+1)/(2k+1)! below this value. * x^(2k+1) < x.ulp; (2k+1)*log10(x) < -x.precision; 2k*log10(x)< -x.precision; * 2k*(-log10(x)) > x.precision; 2k*log10(1/x) > x.precision */ int k = (int) (res.Precision/System.Math.Log10(1.0/res.ToDouble()))/2; var mcTay = new MathContext(ErrorToPrecision(res.ToDouble(), xUlpDbl/k)); for (int i = 1;; i++) { /* TBD: at which precision will 2*i or 2*i+1 overflow? */ ifac = ifac.Multiply(BigInteger.ValueOf(2*i)); ifac = ifac.Multiply(BigInteger.ValueOf((2*i + 1))); xpowi = xpowi.Multiply(res).Multiply(res).Negate(); BigDecimal corr = xpowi.Divide(new BigDecimal(ifac), mcTay); resul = resul.Add(corr); if (corr.Abs().ToDouble() < 0.5*xUlpDbl) break; } /* The error in the result is set by the error in x itself. */ mc = new MathContext(res.Precision); return resul.Round(mc); }
public static Number operator -(Number a) { return(BigDecimal.Negate(new BigDecimal.Config(), a.value)); }
public void TestTruncateOnAllArithmeticOperations() { var savePrecision = BigDecimal.Precision; BigDecimal mod1 = BigDecimal.Parse("3141592653589793238462643383279502"); BigDecimal mod2 = BigDecimal.Parse("27182818284590452"); BigDecimal neg1 = BigDecimal.Parse("-3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117067982148086513282306647"); BigDecimal lrg1 = BigDecimal.Parse("3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117067982148086513282306647"); BigDecimal lrg2 = BigDecimal.Parse("2.718281828459045235360287471352662497757247093699959574966967"); var expected1 = "5.859874482"; var expected2 = "0.4233108251"; var expected3 = "8.5397342226"; var expected4 = "0.8652559794"; var expected5 = "9.869604401"; var expected6 = "148.4131591"; var expected7 = "8003077319547306"; var expected8 = "-3.1415926535"; var expected9 = "3"; var expected10 = "4"; var expected11 = "3.1415926535"; var actual1 = ""; var actual2 = ""; var actual3 = ""; var actual4 = ""; var actual5 = ""; var actual6 = ""; var actual7 = ""; var actual8 = ""; var actual9 = ""; var actual10 = ""; var actual11 = ""; try { BigDecimal.Precision = 10; BigDecimal.AlwaysTruncate = true; TestContext.WriteLine($"E = {BigDecimal.E}"); TestContext.WriteLine($"{new BigDecimal(lrg1.Mantissa, lrg1.Exponent)}"); TestContext.WriteLine($"{new BigDecimal(lrg2.Mantissa, lrg2.Exponent)}"); BigDecimal result1 = BigDecimal.Add(lrg1, lrg2); BigDecimal result2 = BigDecimal.Subtract(lrg1, lrg2); BigDecimal result3 = BigDecimal.Multiply(lrg1, lrg2); BigDecimal result4 = BigDecimal.Divide(lrg2, lrg1); BigDecimal result5 = BigDecimal.Pow(lrg1, 2); BigDecimal result6 = BigDecimal.Exp(new BigInteger(5)); BigDecimal result7 = BigDecimal.Mod(mod1, mod2); BigDecimal result8 = BigDecimal.Negate(lrg1); BigDecimal result9 = BigDecimal.Floor(lrg1); BigDecimal result10 = BigDecimal.Ceiling(lrg1); BigDecimal result11 = BigDecimal.Abs(lrg1); actual1 = result1.ToString(); actual2 = result2.ToString(); actual3 = result3.ToString(); actual4 = result4.ToString(); actual5 = result5.ToString(); actual6 = result6.ToString(); actual7 = result7.ToString(); actual8 = result8.ToString(); actual9 = result9.ToString(); actual10 = result10.ToString(); actual11 = result11.ToString(); } finally { BigDecimal.Precision = savePrecision; BigDecimal.AlwaysTruncate = false; } Assert.AreEqual(expected1, actual1, $"Test Truncate On All Arithmetic Operations - #1: "); Assert.AreEqual(expected2, actual2, $"Test Truncate On All Arithmetic Operations - #2: "); Assert.AreEqual(expected3, actual3, $"Test Truncate On All Arithmetic Operations - #3: "); Assert.AreEqual(expected4, actual4, $"Test Truncate On All Arithmetic Operations - #4: "); Assert.AreEqual(expected5, actual5, $"Test Truncate On All Arithmetic Operations - #5: "); StringAssert.StartsWith(expected6, actual6, $"Test Truncate On All Arithmetic Operations - #6: "); Assert.AreEqual(expected7, actual7, $"Test Truncate On All Arithmetic Operations - #7: "); Assert.AreEqual(expected8, actual8, $"Test Truncate On All Arithmetic Operations - #8: "); Assert.AreEqual(expected9, actual9, $"Test Truncate On All Arithmetic Operations - #9: "); Assert.AreEqual(expected10, actual10, $"Test Truncate On All Arithmetic Operations - #10: "); Assert.AreEqual(expected11, actual11, $"Test Truncate On All Arithmetic Operations - #11: "); Assert.AreEqual(5000, BigDecimal.Precision, "Restore Precision to 5000"); }
public void Negate() { BigDecimal negate1 = new BigDecimal(value2, 7); Assert.IsTrue(negate1.Negate().ToString().Equals("-1233.4560000"), "the negate of 1233.4560000 is not -1233.4560000"); negate1 = BigDecimal.Parse("-23465839"); Assert.IsTrue(negate1.Negate().ToString().Equals("23465839"), "the negate of -23465839 is not 23465839"); negate1 = new BigDecimal(-3.456E6); Assert.IsTrue(negate1.Negate().Negate().Equals(negate1), "the negate of -3.456E6 is not 3.456E6"); }