|
Java example source code file (DfpMath.java)
The DfpMath.java Java example source code/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.math3.dfp; /** Mathematical routines for use with {@link Dfp}. * The constants are defined in {@link DfpField} * @since 2.2 */ public class DfpMath { /** Name for traps triggered by pow. */ private static final String POW_TRAP = "pow"; /** * Private Constructor. */ private DfpMath() { } /** Breaks a string representation up into two dfp's. * <p>The two dfp are such that the sum of them is equivalent * to the input string, but has higher precision than using a * single dfp. This is useful for improving accuracy of * exponentiation and critical multiplies. * @param field field to which the Dfp must belong * @param a string representation to split * @return an array of two {@link Dfp} which sum is a */ protected static Dfp[] split(final DfpField field, final String a) { Dfp result[] = new Dfp[2]; char[] buf; boolean leading = true; int sp = 0; int sig = 0; buf = new char[a.length()]; for (int i = 0; i < buf.length; i++) { buf[i] = a.charAt(i); if (buf[i] >= '1' && buf[i] <= '9') { leading = false; } if (buf[i] == '.') { sig += (400 - sig) % 4; leading = false; } if (sig == (field.getRadixDigits() / 2) * 4) { sp = i; break; } if (buf[i] >= '0' && buf[i] <= '9' && !leading) { sig ++; } } result[0] = field.newDfp(new String(buf, 0, sp)); for (int i = 0; i < buf.length; i++) { buf[i] = a.charAt(i); if (buf[i] >= '0' && buf[i] <= '9' && i < sp) { buf[i] = '0'; } } result[1] = field.newDfp(new String(buf)); return result; } /** Splits a {@link Dfp} into 2 {@link Dfp}'s such that their sum is equal to the input {@link Dfp}. * @param a number to split * @return two elements array containing the split number */ protected static Dfp[] split(final Dfp a) { final Dfp[] result = new Dfp[2]; final Dfp shift = a.multiply(a.power10K(a.getRadixDigits() / 2)); result[0] = a.add(shift).subtract(shift); result[1] = a.subtract(result[0]); return result; } /** Multiply two numbers that are split in to two pieces that are * meant to be added together. * Use binomial multiplication so ab = a0 b0 + a0 b1 + a1 b0 + a1 b1 * Store the first term in result0, the rest in result1 * @param a first factor of the multiplication, in split form * @param b second factor of the multiplication, in split form * @return a × b, in split form */ protected static Dfp[] splitMult(final Dfp[] a, final Dfp[] b) { final Dfp[] result = new Dfp[2]; result[1] = a[0].getZero(); result[0] = a[0].multiply(b[0]); /* If result[0] is infinite or zero, don't compute result[1]. * Attempting to do so may produce NaNs. */ if (result[0].classify() == Dfp.INFINITE || result[0].equals(result[1])) { return result; } result[1] = a[0].multiply(b[1]).add(a[1].multiply(b[0])).add(a[1].multiply(b[1])); return result; } /** Divide two numbers that are split in to two pieces that are meant to be added together. * Inverse of split multiply above: * (a+b) / (c+d) = (a/c) + ( (bc-ad)/(c**2+cd) ) * @param a dividend, in split form * @param b divisor, in split form * @return a / b, in split form */ protected static Dfp[] splitDiv(final Dfp[] a, final Dfp[] b) { final Dfp[] result; result = new Dfp[2]; result[0] = a[0].divide(b[0]); result[1] = a[1].multiply(b[0]).subtract(a[0].multiply(b[1])); result[1] = result[1].divide(b[0].multiply(b[0]).add(b[0].multiply(b[1]))); return result; } /** Raise a split base to the a power. * @param base number to raise * @param a power * @return base<sup>a */ protected static Dfp splitPow(final Dfp[] base, int a) { boolean invert = false; Dfp[] r = new Dfp[2]; Dfp[] result = new Dfp[2]; result[0] = base[0].getOne(); result[1] = base[0].getZero(); if (a == 0) { // Special case a = 0 return result[0].add(result[1]); } if (a < 0) { // If a is less than zero invert = true; a = -a; } // Exponentiate by successive squaring do { r[0] = new Dfp(base[0]); r[1] = new Dfp(base[1]); int trial = 1; int prevtrial; while (true) { prevtrial = trial; trial *= 2; if (trial > a) { break; } r = splitMult(r, r); } trial = prevtrial; a -= trial; result = splitMult(result, r); } while (a >= 1); result[0] = result[0].add(result[1]); if (invert) { result[0] = base[0].getOne().divide(result[0]); } return result[0]; } /** Raises base to the power a by successive squaring. * @param base number to raise * @param a power * @return base<sup>a */ public static Dfp pow(Dfp base, int a) { boolean invert = false; Dfp result = base.getOne(); if (a == 0) { // Special case return result; } if (a < 0) { invert = true; a = -a; } // Exponentiate by successive squaring do { Dfp r = new Dfp(base); Dfp prevr; int trial = 1; int prevtrial; do { prevr = new Dfp(r); prevtrial = trial; r = r.multiply(r); trial *= 2; } while (a>trial); r = prevr; trial = prevtrial; a -= trial; result = result.multiply(r); } while (a >= 1); if (invert) { result = base.getOne().divide(result); } return base.newInstance(result); } /** Computes e to the given power. * a is broken into two parts, such that a = n+m where n is an integer. * We use pow() to compute e<sup>n and a Taylor series to compute * e<sup>m. We return e*n × em * @param a power at which e should be raised * @return e<sup>a */ public static Dfp exp(final Dfp a) { final Dfp inta = a.rint(); final Dfp fraca = a.subtract(inta); final int ia = inta.intValue(); if (ia > 2147483646) { // return +Infinity return a.newInstance((byte)1, Dfp.INFINITE); } if (ia < -2147483646) { // return 0; return a.newInstance(); } final Dfp einta = splitPow(a.getField().getESplit(), ia); final Dfp efraca = expInternal(fraca); return einta.multiply(efraca); } /** Computes e to the given power. * Where -1 < a < 1. Use the classic Taylor series. 1 + x**2/2! + x**3/3! + x**4/4! ... * @param a power at which e should be raised * @return e<sup>a */ protected static Dfp expInternal(final Dfp a) { Dfp y = a.getOne(); Dfp x = a.getOne(); Dfp fact = a.getOne(); Dfp py = new Dfp(y); for (int i = 1; i < 90; i++) { x = x.multiply(a); fact = fact.divide(i); y = y.add(x.multiply(fact)); if (y.equals(py)) { break; } py = new Dfp(y); } return y; } /** Returns the natural logarithm of a. * a is first split into three parts such that a = (10000^h)(2^j)k. * ln(a) is computed by ln(a) = ln(5)*h + ln(2)*(h+j) + ln(k) * k is in the range 2/3 < k <4/3 and is passed on to a series expansion. * @param a number from which logarithm is requested * @return log(a) */ public static Dfp log(Dfp a) { int lr; Dfp x; int ix; int p2 = 0; // Check the arguments somewhat here if (a.equals(a.getZero()) || a.lessThan(a.getZero()) || a.isNaN()) { // negative, zero or NaN a.getField().setIEEEFlagsBits(DfpField.FLAG_INVALID); return a.dotrap(DfpField.FLAG_INVALID, "ln", a, a.newInstance((byte)1, Dfp.QNAN)); } if (a.classify() == Dfp.INFINITE) { return a; } x = new Dfp(a); lr = x.log10K(); x = x.divide(pow(a.newInstance(10000), lr)); /* This puts x in the range 0-10000 */ ix = x.floor().intValue(); while (ix > 2) { ix >>= 1; p2++; } Dfp[] spx = split(x); Dfp[] spy = new Dfp[2]; spy[0] = pow(a.getTwo(), p2); // use spy[0] temporarily as a divisor spx[0] = spx[0].divide(spy[0]); spx[1] = spx[1].divide(spy[0]); spy[0] = a.newInstance("1.33333"); // Use spy[0] for comparison while (spx[0].add(spx[1]).greaterThan(spy[0])) { spx[0] = spx[0].divide(2); spx[1] = spx[1].divide(2); p2++; } // X is now in the range of 2/3 < x < 4/3 Dfp[] spz = logInternal(spx); spx[0] = a.newInstance(new StringBuilder().append(p2+4*lr).toString()); spx[1] = a.getZero(); spy = splitMult(a.getField().getLn2Split(), spx); spz[0] = spz[0].add(spy[0]); spz[1] = spz[1].add(spy[1]); spx[0] = a.newInstance(new StringBuilder().append(4*lr).toString()); spx[1] = a.getZero(); spy = splitMult(a.getField().getLn5Split(), spx); spz[0] = spz[0].add(spy[0]); spz[1] = spz[1].add(spy[1]); return a.newInstance(spz[0].add(spz[1])); } /** Computes the natural log of a number between 0 and 2. * Let f(x) = ln(x), * * We know that f'(x) = 1/x, thus from Taylor's theorum we have: * * ----- n+1 n * f(x) = \ (-1) (x - 1) * / ---------------- for 1 <= n <= infinity * ----- n * * or * 2 3 4 * (x-1) (x-1) (x-1) * ln(x) = (x-1) - ----- + ------ - ------ + ... * 2 3 4 * * alternatively, * * 2 3 4 * x x x * ln(x+1) = x - - + - - - + ... * 2 3 4 * * This series can be used to compute ln(x), but it converges too slowly. * * If we substitute -x for x above, we get * * 2 3 4 * x x x * ln(1-x) = -x - - - - - - + ... * 2 3 4 * * Note that all terms are now negative. Because the even powered ones * absorbed the sign. Now, subtract the series above from the previous * one to get ln(x+1) - ln(1-x). Note the even terms cancel out leaving * only the odd ones * * 3 5 7 * 2x 2x 2x * ln(x+1) - ln(x-1) = 2x + --- + --- + ---- + ... * 3 5 7 * * By the property of logarithms that ln(a) - ln(b) = ln (a/b) we have: * * 3 5 7 * x+1 / x x x \ * ln ----- = 2 * | x + ---- + ---- + ---- + ... | * x-1 \ 3 5 7 / * * But now we want to find ln(a), so we need to find the value of x * such that a = (x+1)/(x-1). This is easily solved to find that * x = (a-1)/(a+1). * @param a number from which logarithm is requested, in split form * @return log(a) */ protected static Dfp[] logInternal(final Dfp a[]) { /* Now we want to compute x = (a-1)/(a+1) but this is prone to * loss of precision. So instead, compute x = (a/4 - 1/4) / (a/4 + 1/4) */ Dfp t = a[0].divide(4).add(a[1].divide(4)); Dfp x = t.add(a[0].newInstance("-0.25")).divide(t.add(a[0].newInstance("0.25"))); Dfp y = new Dfp(x); Dfp num = new Dfp(x); Dfp py = new Dfp(y); int den = 1; for (int i = 0; i < 10000; i++) { num = num.multiply(x); num = num.multiply(x); den += 2; t = num.divide(den); y = y.add(t); if (y.equals(py)) { break; } py = new Dfp(y); } y = y.multiply(a[0].getTwo()); return split(y); } /** Computes x to the y power.<p> * * Uses the following method:<p> * * <ol> * <li> Set u = rint(y), v = y-u * <li> Compute a = v * ln(x) * <li> Compute b = rint( a/ln(2) ) * <li> Compute c = a - b*ln(2) * <li> xy = xu * 2b * ec * </ol> * if |y| > 1e8, then we compute by exp(y*ln(x)) <p> * * <b>Special Cases Other Java examples (source code examples)Here is a short list of links related to this Java DfpMath.java source code file: |
... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.