Giter VIP home page Giter VIP logo

big-math's Introduction

Build Status Code Coverage Quality Gate Status Open Issues Closed Issues Maven Central Last Commits Join the chat at https://gitter.im/eobermuhlner-big-math/community

big-math

Advanced Java BigDecimal math functions (pow, sqrt, log, sin, ...) using arbitrary precision.

See also the official Big-Math Documentation.

BigDecimalMath

The class BigDecimalMath provides efficient and accurate implementations for:

  • log(BigDecimal, MathContext)
  • exp(BigDecimal, MathContext)
  • pow(BigDecimal, BigDecimal, MathContext) calculates x^y
  • sqrt(BigDecimal, MathContext)
  • root(BigDecimal, BigDecimal, MathContext) calculates the n'th root of x
  • sin(BigDecimal, MathContext)
  • cos(BigDecimal, MathContext)
  • tan(BigDecimal, MathContext)
  • asin(BigDecimal, MathContext)
  • acos(BigDecimal, MathContext)
  • atan(BigDecimal, MathContext)
  • atan2(BigDecimal, BigDecimal, MathContext)
  • sinh(BigDecimal, MathContext)
  • cosh(BigDecimal, MathContext)
  • tanh(BigDecimal, MathContext)
  • asinh(BigDecimal, MathContext)
  • acosh(BigDecimal, MathContext)
  • atanh(BigDecimal, MathContext)
  • toDegrees(BigDecimal, MathContext) converts from radians to degrees
  • toRadians(BigDecimal, MathContext) converts from degrees to radians
  • pow(BigDecimal, long, MathContext) calculates x^y for long y
  • factorial(int) calculates n!
  • bernoulli(int) calculates Bernoulli numbers
  • pi(MathContext) calculates pi to an arbitrary precision
  • e(MathContext) calculates e to an arbitrary precision
  • toBigDecimal(String) creates a BigDecimal from string representation (faster than BigDecimal(String))
  • mantissa(BigDecimal) extracts the mantissa from a BigDecimal (mantissa * 10^exponent)
  • exponent(BigDecimal) extracts the exponent from a BigDecimal (mantissa * 10^exponent)
  • integralPart(BigDecimal) extracts the integral part from a BigDecimal (everything before the decimal point)
  • fractionalPart(BigDecimal) extracts the fractional part from a BigDecimal (everything after the decimal point)
  • isIntValue(BigDecimal) checks whether the BigDecimal can be represented as an int value
  • isDoubleValue(BigDecimal) checks whether the BigDecimal can be represented as a double value
  • roundWithTrailingZeroes(BigDecimal, MathContext) rounds a BigDecimal to an arbitrary precision with trailing zeroes.

Usage

Mathematical calculations for BigDecimal

For calculations with arbitrary precision you need to specify how precise you want a calculated result. For BigDecimal calculations this is done using the MathContext.

MathContext mathContext = new MathContext(100);
System.out.println("sqrt(2)        = " + BigDecimalMath.sqrt(BigDecimal.valueOf(2), mathContext));
System.out.println("log10(2)       = " + BigDecimalMath.log10(BigDecimal.valueOf(2), mathContext));
System.out.println("exp(2)         = " + BigDecimalMath.exp(BigDecimal.valueOf(2), mathContext));
System.out.println("sin(2)         = " + BigDecimalMath.sin(BigDecimal.valueOf(2), mathContext));

will produce the following output on the console:

sqrt(2)        = 1.414213562373095048801688724209698078569671875376948073176679737990732478462107038850387534327641573
log10(2)       = 0.3010299956639811952137388947244930267681898814621085413104274611271081892744245094869272521181861720
exp(2)         = 7.389056098930650227230427460575007813180315570551847324087127822522573796079057763384312485079121795
sin(2)         = 0.9092974268256816953960198659117448427022549714478902683789730115309673015407835446201266889249593803

Since many mathematical constants have an infinite number of digits you need to specfiy the desired precision for them as well:

MathContext mathContext = new MathContext(100);
System.out.println("pi             = " + BigDecimalMath.pi(mathContext));
System.out.println("e              = " + BigDecimalMath.e(mathContext));

will produce the following output on the console:

pi             = 3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117068
e              = 2.718281828459045235360287471352662497757247093699959574966967627724076630353547594571382178525166427

Convenience methods for BigDecimal

Additional BigDecimalMath provides several useful methods (that are plain missing for BigDecimal):

MathContext mathContext = new MathContext(100);
System.out.println("mantissa(1.456E99)      = " + BigDecimalMath.mantissa(BigDecimal.valueOf(1.456E99)));
System.out.println("exponent(1.456E99)      = " + BigDecimalMath.exponent(BigDecimal.valueOf(1.456E99)));
System.out.println("integralPart(123.456)   = " + BigDecimalMath.integralPart(BigDecimal.valueOf(123.456)));
System.out.println("fractionalPart(123.456) = " + BigDecimalMath.fractionalPart(BigDecimal.valueOf(123.456)));
System.out.println("isIntValue(123)         = " + BigDecimalMath.isIntValue(BigDecimal.valueOf(123)));
System.out.println("isIntValue(123.456)     = " + BigDecimalMath.isIntValue(BigDecimal.valueOf(123.456)));
System.out.println("isDoubleValue(123.456)  = " + BigDecimalMath.isDoubleValue(BigDecimal.valueOf(123.456)));
System.out.println("isDoubleValue(1.23E999) = " + BigDecimalMath.isDoubleValue(new BigDecimal("1.23E999")));

will produce the following output on the console:

mantissa(1.456E99)      = 1.456
exponent(1.456E99)      = 99
integralPart(123.456)   = 123
fractionalPart(123.456) = 0.456
isIntValue(123)         = true
isIntValue(123.456)     = false
isDoubleValue(123.456)  = true
isDoubleValue(1.23E999) = false

The BigDecimalMath class is thread-safe and can be used in concurrent use cases.

Streams of BigDecimal

The class BigDecimalStream provides factory methods for streams of BigDecimal elements.

Overloaded variants of range(start, end, step) provide sequential elements equivalent to IntStream.range(start, end) but with configurable step (exclusive the end value).

Similar methods for the rangeClosed() (inclusive the end value) are available.

The streams are well behaved when used in parallel mode.

The following code snippet:

System.out.println("Range [0, 10) step 1 (using BigDecimal as input parameters)");
BigDecimalStream.range(BigDecimal.valueOf(0), BigDecimal.valueOf(10), BigDecimal.valueOf(1), mathContext)
	.forEach(System.out::println);

System.out.println("Range [0, 10) step 3 (using long as input parameters)");
BigDecimalStream.range(0, 10, 3, mathContext)
	.forEach(System.out::println);

produces this output:

Range [0, 10) step 1 (using BigDecimal as input parameters)
0
1
2
3
4
5
6
7
8
9

Range [0, 12] step 3 (using long as input parameters)
0
3
6
9
12

FAQ

Why do I have to pass MathContext to most functions?

Many mathematical functions have results that have many digits (often an infinite number of digits). When calculating these functions you need to specify the number of digits you want in the result, because calculating an infinite number of digits would take literally forever and consume an infinite amount of memory.

The MathContext contains a precision and information on how to round the last digits, so it is an obvious choice to specify the desired precision of mathematical functions.

What if I really do not want to pass the MathContext everytime?

The convenience class DefaultBigDecimalMath was added that provides mathematical functions where the MathContext must not be passed every time.

Please refer to the chapter DefaultBigDecimalMath

I specified a precision of n digits, but the results have completely different number of digits after the decimal point. Why?

It is a common misconception that the precision defines the number of digits after the decimal point.

Instead the precision defines the number of relevant digits, independent of the decimal point. The following numbers all have a precision of 3 digits:

  • 12300
  • 1230
  • 123
  • 12.3
  • 1.23
  • 0.123
  • 0.0123

To specify the number of digits after the decimal point use BigDecimal.setScale(scale, mathContext).

Why are BigDecimalMath functions so slow?

The mathematical functions in BigDecimalMath are heavily optimized to calculate the result in the specified precision, but in order to calculate them often tens or even hundreds of basic operations (+, -, *, /) using BigDecimal are necessary.

If the calculation of your function is too slow for your purpose you should verify whether you really need the full precision in your particular use case. Sometimes you can adapt the precision depending on input factors of your calculation.

How are the mathematical functions in BigDecimalMath calculated?

For the mathematical background and performance analysis please refer to this article:

Some of the implementation details are explained here:

Why is there BigDecimalMath.toBigDecimal(String) if Java already has a BigDecimal(String) constructor?

The BigDecimal(String) constructor as provided by Java gets increasingly slower if you pass longer strings to it. The implementation in Java 11 and before is O(n^2).

If you want to convert very long strings (10000 characters or longer) then this slow constructor may become an issue.

BigDecimalMath.toBigDecimal(String) is a drop-in replacement with the same functionality (converting a string representation into a BigDecimal) but it is using a faster recursive implementation.

The following chart shows the time necessary to create a BigDecimal from a string representation of increasing length: toBigDecimal() precisions 0 to 100000

I only need a sqrt function - should I use this library?

Since Java 9 the BigDecimal class has a new function sqrt(BigDecimal, MathContext). If you only need the square root function then by all means use the provided standard function instead of this library.

If you need any other high level function then you should still consider using this library.

For high precision (above 150 digits) the current implementation of Java 9 BigDecimal.sqrt() becomes increasingly slower than BigDecimalMath.sqrt(). You should consider whether the increased performance is worth having an additional dependency.

The following charts shows the time needed to calculate the square root of 3.1 with increasing precision.

Java9 sqrt vs. sqrt() Java9 sqrt vs. sqrt()

Performance

The following charts show the time needed to calculate the functions over a range of values with a precision of 300 digits.

sqrt(), root(), exp(), sin(), cos() -10 to 10 sqrt(), root(), exp(), sin(), cos() 0 to 10 sqrt(), root(), exp(), sin(), cos() 0 to 100

exp(), log(), pow() 0 to 10 exp(), log(), pow() 0 to 100

BigComplex

The class BigComplex represents complex numbers in the form (a + bi). It follows the design of BigDecimal with some convenience improvements like overloaded operator methods.

A big difference to BigDecimal is that BigComplex.equals() implements the mathematical equality and not the strict technical equality. This was a difficult decision because it means that BigComplex behaves slightly different than BigDecimal but considering that the strange equality of BigDecimal is a major source of bugs we decided it was worth the slight inconsistency.

If you need the strict equality use BigComplex.strictEquals().

  • re
  • im
  • add(BigComplex)
  • add(BigComplex, MathContext)
  • add(BigDecimal)
  • add(BigDecimal, MathContext)
  • add(double)
  • subtract(BigComplex)
  • subtract(BigComplex, MathContext)
  • subtract(BigDecimal)
  • subtract(BigDecimal, MathContext)
  • subtract(double)
  • multiply(BigComplex)
  • multiply(BigComplex, MathContext)
  • multiply(BigDecimal)
  • multiply(BigDecimal, MathContext)
  • multiply(double)
  • divide(BigComplex)
  • divide(BigComplex, MathContext)
  • divide(BigDecimal)
  • divide(BigDecimal, MathContext)
  • divide(double)
  • reciprocal(MathContext)
  • conjugate()
  • negate()
  • abs(MathContext)
  • angle(MathContext)
  • absSquare(MathContext)
  • isReal()
  • re()
  • im()
  • round(MathContext)
  • hashCode()
  • equals(Object)
  • strictEquals(Object)
  • toString()
  • valueOf(BigDecimal)
  • valueOf(double)
  • valueOf(BigDecimal, BigDecimal)
  • valueOf(double, double)
  • valueOfPolar(BigDecimal, BigDecimal, MathContext)
  • valueOfPolar(double, double, MathContext

BigComplexMath

The class BigComplexMath is the equivalent of BigDecimalMath and contains mathematical functions in the complex domain.

  • sin(BigComplex, MathContext)
  • cos(BigComplex, MathContext)
  • tan(BigComplex, MathContext)
  • asin(BigComplex, MathContext)
  • acos(BigComplex, MathContext)
  • atan(BigComplex, MathContext)
  • acot(BigComplex, MathContext)
  • exp(BigComplex, MathContext)
  • log(BigComplex, MathContext)
  • pow(BigComplex, long, MathContext)
  • pow(BigComplex, BigDecimal, MathContext)
  • pow(BigComplex, BigComplex, MathContext)
  • sqrt(BigComplex, MathContext)
  • root(BigComplex, BigDecimal, MathContext)
  • root(BigComplex, BigComplex, MathContext)

DefaultBigDecimalMath

The class DefaultBigDecimalMath is a wrapper around BigDecimalMath that passes always the current MathContext to the functions that need a MathContext argument.

The initial default MathContext is equivalent to MathContext.DECIMAL128 but this can be overridden by setting the following system properties:

  • ch.obermuhlner.math.big.default.precision to a positive integer precision (default=34)
  • ch.obermuhlner.math.big.default.rounding to a RoundingMode name (default=HALF_UP)

It is also possible to programmatically set the default MathContext using setDefaultMathContext(MathContext). It is recommended to set the desired precision in the MathContext very early in the startup of the application and to not change it afterwards.

Important: Avoid the pitfall of setting the precision temporarily using setDefaultMathContext(MathContext) for a calculation. This can lead to race conditions and calculations with the wrong precision if other threads in your application do the same thing.

To set a temporary MathContext you have to choice to use either:

  • DefaultBigDecimalMath.createLocalMathContext() in a try-with-resources statement
  • DefaultBigDecimalMath.withLocalMathContext() with a lambda function

Example code using DefaultBigDecimalMath.createLocalMathContext():

System.out.println("Pi[default]: " + DefaultBigDecimalMath.pi());
try (DefaultBigDecimalMath.LocalMathContext context = DefaultBigDecimalMath.createLocalMathContext(5)) {
    System.out.println("Pi[5]: " + DefaultBigDecimalMath.pi());
    try (DefaultBigDecimalMath.LocalMathContext context2 = DefaultBigDecimalMath.createLocalMathContext(10)) {
        System.out.println("Pi[10]: " + DefaultBigDecimalMath.pi());
    };
    System.out.println("Pi[5]: " + DefaultBigDecimalMath.pi());
};
System.out.println("Pi[default]: " + DefaultBigDecimalMath.pi());

Example code using DefaultBigDecimalMath.withLocalMathContext():

System.out.println("Pi[default]: " + DefaultBigDecimalMath.pi());
DefaultBigDecimalMath.withLocalMathContext(5, () -> {
    System.out.println("Pi[5]: " + DefaultBigDecimalMath.pi());
    DefaultBigDecimalMath.withLocalMathContext(10, () -> {
        System.out.println("Pi[10]: " + DefaultBigDecimalMath.pi());
    });
    System.out.println("Pi[5]: " + DefaultBigDecimalMath.pi());
});
System.out.println("Pi[default]: " + DefaultBigDecimalMath.pi());

Both snippets will give the following output:

Pi[default]: 3.141592653589793238462643383279503
Pi[5]: 3.1416
Pi[10]: 3.141592654
Pi[5]: 3.1416
Pi[default]: 3.141592653589793238462643383279503

The temporary MathContext are stored in ThreadLocal variables and will therefore not conflict with each other when used in multi-threaded use case.

Important: Due to the ThreadLocal variables the temporary MathContext will not be available in other threads. This includes streams using parallel(), thread pools and manually started threads. If you need temporary MathContext for calculations then you must set the local MathContext inside every separate thread.

try (DefaultBigDecimalMath.LocalMathContext context = DefaultBigDecimalMath.createLocalMathContext(5)) {
    BigDecimalStream.range(0.0, 1.0, 0.01, DefaultBigDecimalMath.currentMathContext())
            .map(b -> DefaultBigDecimalMath.cos(b))
            .map(b -> "sequential " + Thread.currentThread().getName() + " [5]: " + b)
            .forEach(System.out::println);

    BigDecimalStream.range(0.0, 1.0, 0.01, DefaultBigDecimalMath.currentMathContext())
            .parallel()
            .map(b -> {
                try (DefaultBigDecimalMath.LocalMathContext context2 = DefaultBigDecimalMath.createLocalMathContext(5)) {
                    return DefaultBigDecimalMath.cos(b);
                }
            })
            .map(b -> "parallel " + Thread.currentThread().getName() + " [5]: " + b)
            .forEach(System.out::println);
}

BigFloat

The class BigFloat is an experimental wrapper around BigDecimal which simplifies the consistent usage of the MathContext and provides a simpler API for calculations.

The API for calculations is simplified and more consistent with the typical mathematical usage.

  • Factory methods on the Context class for values:

    • valueOf(BigFloat)
    • valueOf(BigDecimal)
    • valueOf(int)
    • valueOf(long)
    • valueOf(double)
    • valueOf(String)
    • pi()
    • e()
  • All standard operators:

    • add(x)
    • subtract(x)
    • multiply(x)
    • divide(x)
    • remainder(x)
    • pow(y)
    • root(y)
  • Calculation methods are overloaded for different value types:

    • add(BigFloat)
    • add(BigDecimal)
    • add(int)
    • add(long)
    • add(double)
    • ...
  • Mathematical functions are written in the traditional form:

    • abs(x)
    • log(x)
    • sin(x)
    • min(x1, x2, ...)
    • max(x1, x2, ...)
    • ...
  • Support for advanced mathematical functions:

    • sqrt(x)
    • log(x)
    • exp(x)
    • sin(x)
    • cos(x)
    • tan(x)
    • ...
  • Methods to access parts of a value:

    • getMantissa()
    • getExponent()
    • getIntegralPart()
    • getFractionalPart()
  • Methods to verify value type conversions:

    • isIntValue()
    • isDoubleValue()
  • Equals and Hashcode methods:

    • equals(Object) returns whether two BigFloat values are mathematically the same
    • hashCode() consistent with equals(Object)
  • Comparison methods:

    • isEqual(BigFloat)
    • isLessThan(BigFloat)
    • isLessThanOrEqual(BigFloat)
    • isGreaterThan(BigFloat)
    • isGreaterThanOrEqual(BigFloat)
  • Sign methods:

    • signum()
    • isNegative()
    • isZero()
    • isPositive()

Usage

Before doing any calculations you need to create a Context specifying the precision used for all calculations.

Context context = BigFloat.context(100); // precision of 100 digits
Context anotherContext = BigFloat.context(new MathContext(10, RoundingMode.HALF_UP); // precision of 10 digits, rounding half up

The Context can then be used to create the first value of the calculation:

BigFloat value1 = context.valueOf(640320);

The BigFloat instance holds a reference to the Context. This context is then passed from calculation to calculation.

BigFloat value2 = context.valueOf(640320).pow(3).divide(24);
BigFloat value3 = BigFloat.sin(value2);

The BigFloat result can be converted to other numerical types:

BigDecimal bigDecimalValue = value3.toBigDecimal();
double doubleValue = value3.toDouble();
long longValue = value3.toLong();
int intValue = value3.toInt();

The BigFloatStream provides similar stream factories as BigDecimalStream that will produce streams of BigFloat elements.

Usage in Java Module Systems (Jigsaw and OSGi)

Since release 2.0.1 the deployed big-math Jar file contains now a module name for the Jigsaw module system (Java 9 and later).

This allows it to be used as automatic module with a well defined module name instead of deriving the name magically from the Jar file name.

The module name follows the reverse domain convention and is: ch.obermuhlner.math.big

The big-math Jar file is also OSGi compatible.

The MANIFEST.MF contains all the necessary headers and exports the public packages:

  • ch.obermuhlner.math.big
  • ch.obermuhlner.math.big.stream

Usage in Kotlin

If you want to use big-math library in Kotlin you may do so directly, or you use the kotlin-big-math library that provides additional features, like operators.

Using big-math in your projects

To use the Java library you can either download the newest version of the .jar file from the published releases on Github or use the following dependency to Maven Central in your build script (please verify the version number to be the newest release):

Use big-math in Maven Build

<dependency>
    <groupId>ch.obermuhlner</groupId>
    <artifactId>big-math</artifactId>
    <version>2.3.2</version>
</dependency>

Use big-math in Gradle Build

repositories {
  mavenCentral()
}

dependencies {
  compile 'ch.obermuhlner:big-math:2.3.2'
}

big-math's People

Contributors

agdturner avatar breandan avatar dependabot[bot] avatar eobermuhlner avatar gitter-badger avatar kspalaiologos avatar modestas-4finance avatar theswapnilsaste avatar uklimaschewski avatar vova7878 avatar wireless4024 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

big-math's Issues

Prepare release 2.0.0

  • rename release note
  • change version in gradle.build
  • build and commit javadoc
  • upload artifacts to maven central
    • uncomment task uploadArchives in gradle.build
    • run ./gradlew uploadArchives
    • go to https://oss.sonatype.org/
    • in tab 'Staging Repositories' locate own Repository (typically at the end of the list)
    • verify content of own Repository (version number!)
    • Close own Repository
    • Refresh
    • Release own Repository
  • create github release from same artifacts
    • Create new draft release
    • Copy content of release note into draft release
    • Add artefacts from gradle build to draft release
    • Publish release
  • update readme
  • update docs/index.md
  • update dependent projects
  • create empty release note for next release

license

What is the license for your code?

might has issue with MathContext.UNLIMITED

here is MathContext.UNLIMITED
public static final MathContext UNLIMITED = new MathContext(0, RoundingMode.HALF_UP);
precision is zero then
public static BigDecimal pow(BigDecimal x, long y, MathContext mathContext) { MathContext mc = new MathContext(mathContext.getPrecision() + 10, mathContext.getRoundingMode());
it changed form Unlimited to 10 digits precision
maybe change it to
MathContext mc = new MathContext(mathContext.getPrecision()==0?65535/*<<some big number?*/:mathContext.getPrecision(), mathContext.getRoundingMode());
maybe
if(mathContext.getPrecision()==0) throw new UnsupportedOperationException("Unlimited MathContext is not supported");

Factorial method has an incorrect method signature

The implementation of the factorial method provides only an integer argument, which does not match the definition found in the README documentation.

The method should use factorial(int, MathContext) rather than factorial(int), according to the documentation.

Additionally, for the sake of consistency, I think the factorial method should take a BigDecimal rather than an integer. Inputting a fractional factorial should simply use the gamma function.

Prepare release 2.0.1

  • add release number header to release note
  • rename release note
  • change version in build.gradle
  • upload artifacts to maven central
    • uncomment task uploadArtifacts in build.gradle
    • run ./gradlew :ch.obermuhlner.math.big:uploadArchives
    • go to https://oss.sonatype.org/
    • in tab 'Staging Repositories' locate own Repository (typically at the end of the list)
    • verify content of own Repository (version number!)
    • Close own Repository
    • Refresh
    • Release own Repository
  • create github release from same artifacts
    • Create new draft release
    • Copy content of release note into draft release
    • Add artefacts from gradle build to draft release
      • big-math-*.jar
      • big-math-*-javadoc.jar
      • big-math-*-sources.jar
    • Publish release
  • update readme
  • add generated javadoc to docs/javadoc
  • update docs/index.md
  • update dependent projects
  • create empty release note for next release

Prepare release 2.2.0

  • run performance benchmarks, create png, commit
  • add release number header to release note
  • rename release note
  • change version in build.gradle
  • upload artifacts to maven central
    • uncomment task uploadArtifacts in build.gradle
    • run ./gradlew clean
    • run ./gradlew :ch.obermuhlner.math.big:uploadArchives
    • comment task uploadArtifacts in build.gradle
    • go to https://oss.sonatype.org/
    • in tab 'Staging Repositories' locate own Repository (typically at the end of the list)
    • verify content of own Repository (version number!)
    • Close own Repository
    • Refresh until Release becomes enabled
    • Release own Repository
  • create github release from same artifacts
    • Create new draft release
    • Copy content of release note into draft release
    • Add artefacts from gradle build to draft release
      • big-math-*.jar
      • big-math-*-javadoc.jar
      • big-math-*-sources.jar
    • Publish release
  • update readme
  • add generated javadoc to docs/javadoc
  • update docs/index.md
  • update dependent projects
  • create empty release note for next release

doubleValue performance

I know that this is not related to the code on this repo, but I just wanted to ask if there is a way to implement a function that returns the double value approximation of a bigdecimal. The original implementation (at least for java 8 uses string operations in order to construct that representation which makes it incredibly slow).

If nothing can be done, then please go ahead and close this ticket.

Thank you very much

BigDecimalMath.log(new BigDecimal("1E-399"), MC) fails

java.lang.NumberFormatException
	at java.math.BigDecimal.<init>(BigDecimal.java:494)
	at java.math.BigDecimal.<init>(BigDecimal.java:383)
	at java.math.BigDecimal.<init>(BigDecimal.java:806)
	at java.math.BigDecimal.valueOf(BigDecimal.java:1274)
	at ch.obermuhlner.math.big.BigDecimalMath.logUsingNewton(BigDecimalMath.java:525)
	at ch.obermuhlner.math.big.BigDecimalMath.log(BigDecimalMath.java:478)

Something the matter with the 2.3.1 release?

I noticed that using the 2.3.1 things like this happen:

BigDecimal pow = BigDecimalMath.pow(new BigDecimal("200"), new BigDecimal("-200"), new MathContext(6));
System.out.println("" + pow);

prints 0, which would obviously be a bug. I thought I'd look into it, and it costed me some time, but the code which is on maven central as 2.3.1 for this class doesn't seem to be right.

E.g. thispow function ends with
return roundAbsolutePrecision(result, mathContext); (which btw seems to cause the problem)

which I can't even find in the repository.

I don't know what happened, but something seems to be rather odd?

Prepare release 2.1.0

  • run performance benchmarks, create png, commit
  • add release number header to release note
  • rename release note
  • change version in build.gradle
  • upload artifacts to maven central
    • uncomment task uploadArtifacts in build.gradle
    • run ./gradlew :ch.obermuhlner.math.big:uploadArchives
    • go to https://oss.sonatype.org/
    • in tab 'Staging Repositories' locate own Repository (typically at the end of the list)
    • verify content of own Repository (version number!)
    • Close own Repository
    • Refresh
    • Release own Repository
  • create github release from same artifacts
    • Create new draft release
    • Copy content of release note into draft release
    • Add artefacts from gradle build to draft release
      • big-math-*.jar
      • big-math-*-javadoc.jar
      • big-math-*-sources.jar
    • Publish release
  • update readme
  • add generated javadoc to docs/javadoc
  • update docs/index.md
  • update dependent projects
  • create empty release note for next release

Optimize performance of `log()`

In release 2.0.0 the calculation precision was improved at the cost of calculation performance.

Optimize performance of log() without sacrificing calculation precision.

BigRational toFloat() can fail in a slightly unexpected way

Consider the following example which shows why the method fails and an alternative (Float.valueOf(x.toBigDecimal().toString())) that works more how I expected, but would fail if x could not be represented as a BigDecimal.
BigRational x = BigRational.valueOf("8.804462619980757911125181749462772084351754080848495898653087767533866267556634");
System.out.println(x.toRationalString());
System.out.println(x.toFloat());
System.out.println(Float.valueOf(x.toBigDecimal().toString()));
System.out.println(new BigDecimal("1000000000000000000000000000000000000000000000000000000000000000000000000000000").floatValue());

sqrt hangs

Thanks for a great library.

Testing with OpenJDK 11 and 15, BigDecimalMath.sqrt seems to go into an infinite loop when passed any of these values when the precision and rounding mode are as follows:

MathContext mc = new MathContext(10, RoundingMode.HALF_UP);
System.out.println("Testing...");
System.out.println( BigDecimalMath.sqrt( new BigDecimal("10000000000000005"), mc ) );
System.out.println( BigDecimalMath.sqrt( new BigDecimal("10000000000000006"), mc ) );
System.out.println( BigDecimalMath.sqrt( new BigDecimal("10000000000000007"), mc ) );
System.out.println( BigDecimalMath.sqrt( new BigDecimal("10000000000000008"), mc ) );
System.out.println( BigDecimalMath.sqrt( new BigDecimal("10000000000000009"), mc ) );

sqrt rounding

BigDecimalMath.sqrtrounding isn't always correct. Three examples:

// OpenJDK 15
BigDecimal x;
MathContext mc10 = new MathContext(10, RoundingMode.HALF_UP);
MathContext mc30 = new MathContext(30, RoundingMode.DOWN);
MathContext mc40 = new MathContext(40, RoundingMode.HALF_UP);
MathContext mc60 = new MathContext(60, RoundingMode.DOWN);

System.out.println("MathContext(10, RoundingMode.HALF_UP)");
System.out.println();

x = new BigDecimal("400000005");
System.out.println("BigDecimal sqrt:      " + x.sqrt(mc10));
System.out.println("BigDecimalMath sqrt:  " + BigDecimalMath.sqrt(x, mc10));
System.out.println("Reference value:      " + BigDecimalMath.sqrt(x, mc30) + "...");
System.out.println();

x = new BigDecimal("63999999");
System.out.println("BigDecimal sqrt:      " + x.sqrt(mc10));
System.out.println("BigDecimalMath sqrt:  " + BigDecimalMath.sqrt(x, mc10));
System.out.println("Reference value:      " + BigDecimalMath.sqrt(x, mc30) + "...");
System.out.println();

System.out.println("MathContext(40, RoundingMode.HALF_UP)");
System.out.println();

x = new BigDecimal("1000000000001");
System.out.println("BigDecimal sqrt:      " + x.sqrt(mc40));
System.out.println("BigDecimalMath sqrt:  " + BigDecimalMath.sqrt(x, mc40));
System.out.println("Reference value:      " + BigDecimalMath.sqrt(x, mc60) + "...");
System.out.println();


MathContext(10, RoundingMode.HALF_UP)

BigDecimal sqrt:      20000.00012
BigDecimalMath sqrt:  20000.00013
Reference value:      20000.0001249999996093750024414...

BigDecimal sqrt:      7999.999937
BigDecimalMath sqrt:  7999.999938
Reference value:      7999.99993749999975585937309265...

MathContext(40, RoundingMode.HALF_UP)

BigDecimal sqrt:      1000000.000000499999999999875000000000062
BigDecimalMath sqrt:  1000000.000000499999999999875000000000063
Reference value:      1000000.00000049999999999987500000000006249999999996093750000...

BigComplexMath.sqrt fails on non-positive real numbers

When BigComplexMath.sqrt is invoked with an argument that has a negative or zero real part and a zero imaginary part, it throws an ArithmeticException:

BigComplexMath.sqrt(BigComplex.ONE.negate(), MathContext.DECIMAL64)
Exception in thread "main" java.lang.ArithmeticException: Division undefined
	at java.base/java.math.BigDecimal.divide(BigDecimal.java:1831)
	at ch.obermuhlner.math.big.BigComplex.divide(BigComplex.java:287)
	at ch.obermuhlner.math.big.BigComplexMath.sqrt(BigComplexMath.java:289)

Expected behaviour:

BigComplexMath.sqrt should not throw an ArithmeticException but compute the result (in the above case 0 + 1i) instead.

Prepare release 2.3.1

  • add release number header to release note
  • rename release note
  • create empty release note for next release from template_release_note.md
  • change version in build.gradle of big-math subproject
  • change version in pom.xml
  • upload artifacts to maven central
    • run ./gradlew clean
    • run ./gradlew :ch.obermuhlner.math.big:uploadArchives
    • go to https://oss.sonatype.org/
    • in tab 'Staging Repositories' locate own Repository (typically at the end of the list)
    • verify content of own Repository (version number!)
    • Close own Repository
    • Refresh until Release becomes enabled
    • Release own Repository
  • create github release from same artifacts
    • Create new draft release
    • Copy content of release note into draft release
    • Add artefacts from gradle build to draft release
      • big-math-*.jar
      • big-math-*-javadoc.jar
      • big-math-*-sources.jar
    • Verify all changes are committed and pushed
    • Create a new tag in release
    • Publish release
  • update readme
  • add generated javadoc to docs/javadoc
  • update docs/index.md
  • update dependent projects
  • create regression project for new release
    • edit new build.gradle to use new release library
    • edit settings.gradle to add new regression project
    • run ./run_regression_analysis.sh

Nullpointer exception in SeriesCalculator 121 line

Hello,
I have experienced nullpointer exeption in SeriesCalculator 121 line, because factor of index 15 was not found...

Java version jre_8u201

SeriesCalculator BigDecimal calculate(BigDecimal x, MathContext mathContext) method was receiving these parameters:

BigDecimal x = 0.00027737357367408531168137439716441660398990660211765625
MathContext mathContext = new MathContext(66, RoundingMode.HALF_EVEN)

Factor array had 17 elements, and was trying to get 15th element, but it was null. Image is attached of formed factors array.

image (1)

Your library was used with this enpoint:

BigDecimal irr = new BigDecimal("1.0735999999999999999999999808121954095390385925")
BigDecimal numberOfParts = new BigDecimal("12.16666666666666666666666666666667");
BigDecimalMath.pow(irr, numberOfParts, DECIMAL128)

I cannot replicate this situation in tests locally... It is so strange... Locally using openjdk 1.8.0_201

Thank you.

Issue with larger scales?

I'm currently working on using your excellent library for my EvalEx project.
While porting the acos() function, I came to some kind of rounding problems, I guess.
Given this code:

    MathContext mathContext = new MathContext(68, RoundingMode.HALF_EVEN);
    BigDecimal x = new BigDecimal("1", mathContext);
    BigDecimal acos = BigDecimalMath.acos(x, mathContext);

    System.out.println(acos.toPlainString());

it will not print 0 as expected, but it prints out 0.0000000000000000000000000000000000000000000000000000000000000000000000001

This only happens, when I use a scale >= 60 in the MathContext.

Is this a problem of big-math or BigDecimal?

Prepare release 2.2.1

  • add release number header to release note
  • rename release note
  • create empty release note for next release from template_release_note.md
  • change version in build.gradle
  • upload artifacts to maven central
    • run ./gradlew clean
    • run ./gradlew :ch.obermuhlner.math.big:uploadArchives
    • go to https://oss.sonatype.org/
    • in tab 'Staging Repositories' locate own Repository (typically at the end of the list)
    • verify content of own Repository (version number!)
    • Close own Repository
    • Refresh until Release becomes enabled
    • Release own Repository
  • create github release from same artifacts
    • Create new draft release
    • Copy content of release note into draft release
    • Add artefacts from gradle build to draft release
      • big-math-*.jar
      • big-math-*-javadoc.jar
      • big-math-*-sources.jar
    • Verify all changes are committed and pushed
    • Publish release
  • update readme
  • add generated javadoc to docs/javadoc
  • update docs/index.md
  • update dependent projects
  • create regression project for new release
    • edit new build.gradle to use new release library
    • edit settings.gradle to add new regression project
    • run ./gradlew :regression.v2_2_1:run (verify version number) with standard machine
    • edit analysis/*.csv files to add new release numbers
    • run csv2chart --property chart=line *.csv in directory regression/analysis

Performance issue in BigDecimalMath.pow()

The BigDecimalMath.pow() method is relying on Exception handling to recover from some corner cases.
In my profiling the BigDecimal.longValueExcact() can throw ArithmeticException("Rounding necessary") which takes a large chunk of the BigDecimalMath.pow() execution time. The time is X axis on the flame graph below - roughly 50%?

image

The issue seem to be with try/catch code below and I wonder if the exception could be mitigated with some conditions inside try/catch to avoid some common cases of the ArithmeticException?

image

BigComplexMath#sqrt fails when x is a negative real number

public static BigComplex sqrt(BigComplex x, MathContext mathContext) {

This fails when x is a negative real number because you are dividing (x+abs(x))/abs(x+abs(x)) which is 0/0.

Maybe these are helpful: https://mathworld.wolfram.com/SquareRoot.html and org.apache.commons.numbers.complex.Complex#sqrt() from org.apache.commons:commons-numbers-complex.

Problem in atan2 function

Hello

I think there may be a little typo in atan2() . On line 1033 of BigDecimalMath there is

return atan(y.divide(x, 3), mathContext);

which I think should be

return atan(y.divide(x, mc), mathContext);

[question] Double.POSITIVE_INFINITE, isNAN functions available?

This is some kind of library! good stuff.

I have a question about having representation of following Double only related JDK functions:

  • POSITIVE_INFINITE,
  • isNan()

I have especially problem with positive_infinite representation in BigDecimal...

What do you think to add this constant from Double into your library as method with mathcontext as additional parameter for precision config.

Publish to Maven

Hey, i'm working on a project and got in a situation where a i need the log function over a BigDecimal, found your project and seems great you have done a excellent work, but the limitation for me is that i can only add dependencies which are in a Maven repository, it would be great if you could publish it even if is in a beta stage, Thank you! and keep the good work!.

Provide a solution where MathContext does not need to be passed to all methods

Copy paste from blog comment:

At the moment, BigDecimalMath requires you to submit a java.math.MathContext
every single time. Is it possible for you to add the option to omit that,
or have the situation so that users might submit or overwrite a MAthContext
once at the beginning, and include only the number parameters for the functions
inside BigDecimalMath?

Prepare release 1.3.0

  • rename release note
  • change version in gradle.build
  • upload artifacts to maven central
    • uncomment task uploadArtifacts in gradle.build
    • run ./gradlew uploadArtifacts
    • go to https://oss.sonatype.org/
    • in tab 'Staging Profiles' locate own Repository (typically at the end of the list)
    • verify content of own Repository (version number!)
    • Close own Repository
    • Refresh
    • Release own Repository
  • create github release from same artifacts
    • Create new draft release
    • Copy content of release note into draft release
    • Add artefacts from gradle build to draft release
    • Publish release
  • update readme
  • update dependent projects
  • create empty release note for next release

Prepare release 2.3.0

  • add release number header to release note
  • rename release note
  • create empty release note for next release from template_release_note.md
  • change version in build.gradle
  • change version in pom.xml
  • upload artifacts to maven central
    • run ./gradlew clean
    • run ./gradlew :ch.obermuhlner.math.big:uploadArchives
    • go to https://oss.sonatype.org/
    • in tab 'Staging Repositories' locate own Repository (typically at the end of the list)
    • verify content of own Repository (version number!)
    • Close own Repository
    • Refresh until Release becomes enabled
    • Release own Repository
  • create github release from same artifacts
    • Create new draft release
    • Copy content of release note into draft release
    • Add artefacts from gradle build to draft release
      • big-math-*.jar
      • big-math-*-javadoc.jar
      • big-math-*-sources.jar
    • Verify all changes are committed and pushed
    • Publish release
  • update readme
  • add generated javadoc to docs/javadoc
  • update docs/index.md
  • update dependent projects
  • create regression project for new release
    • edit new build.gradle to use new release library
    • edit settings.gradle to add new regression project
    • run ./run_regression_analysis.sh

Doesn't work for the code below

BigDecimal number = new BigDecimal("-68.42");

BigDecimal otherNumber = new BigDecimal("0.30274591");

BigDecimalMath.pow(number,otherNumber,new MathContext(100));

The Android's calculator can handle such an expression:

whatsapp image 2018-09-02 at 16 00 36

PS.: version: 2.0.1

Use BigDecimal.compareTo() insted of BigDecimal.equals()

The java.math.BigDecimal.equals() method compares this BigDecimal with the specified Object for equality. Unlike compareTo, this method considers two BigDecimal objects equal only if they are equal in value and scale (thus 2.0 is not equal to 2.00 when compared by this method).

Support complex numbers

Add a class BigComplex (or similar name, maybe BigDecimalComplex) with basic arithmetic support - similar design to BigDecimal.

Add a class BigComplexMath with static math functions in the complex domain.

Question about big-math .jar file.

-I can see where to to obtain the javadoc for big-math,
Where can I find a free .jar build use file for for the latest
stable version of the big-math library that will work
with Java OpenJDK 19?

Prepare release 2.3.2

  • add release number header to release note
  • rename release note
  • create empty release note for next release from template_release_note.md
  • change version in build.gradle of big-math subproject
  • change version in pom.xml
  • upload artifacts to maven central
    • run ./gradlew clean
    • run ./gradlew :ch.obermuhlner.math.big:uploadArchives
    • go to https://oss.sonatype.org/
    • in tab 'Staging Repositories' locate own Repository (typically at the end of the list)
    • verify content of own Repository (version number!)
    • Close own Repository
    • Refresh until Release becomes enabled
    • Release own Repository
  • create github release from same artifacts
    • Create new draft release
    • Create new tag "v1.2.3" (will be created when published)
    • Create new title for release e.g. "Release 1.2.3"
    • Copy content of release note into draft release (without the first header "# Release 1.2.3")
    • Add artefacts from gradle build to draft release
      • big-math-*.jar
      • big-math-*-javadoc.jar
      • big-math-*-sources.jar
    • Verify all changes are committed and pushed
    • Publish release
  • update readme
  • add generated javadoc to docs/javadoc (copy folder javadoc from build/docs and renamed to e.g. v1.2.3)
  • update docs/index.md
  • update dependent projects
  • create regression project for new release
    • edit new build.gradle to use new release library
    • edit settings.gradle to add new regression project
    • run ./run_regression_analysis.sh
  • commit and push chages

`BigRational.toIntegerRationalString()` with small negative numbers

Negative rational numbers smaller than 1 are printed without - sign.

For example:

BigRational v = valueOf(-1, 2);
System.out.println("small negative rational toString(): " + v);
System.out.println("small negative rational toIntegerRationalString(): " + v);

prints:

small negative rational toString(): -0.5
small negative rational toIntegerRationalString(): 1/2

Questions about the big-math library: Capabilities and Future.

-In terms of base 10 positive intetgers, is there a maximum positive integer that your library can load into memory and operate with?

-In terms of base 10 positive decimals (only), is there a minimum minute value that your library can load into memory and operate with?

-Full completion of the project Valhalla for Java along with OpenJDK promises value types and primitive types. I have gathered that it is possible to set up mathematics with the big-math library so that every operation does not require the precision to be submitted along as well, but that can be managed from one place within scope before the beginning of mathematics. If Valhalla allows operator inclusion with its new primitive types and other new advantages, are the big-math project developers likely going to be persuaded to incorporated into big-math? Operators like +,-,*, both modes of /, %, i++, i--, --i, ++i, +=, -=, *=, /= both modes, %= ?

root(x, n) with large n is very slow

The BigDecimalMath.root(BigDecimal, BigDecimal, MathContext) function converges very slowly for larger values of n.

This is due to a bad initial value for the Newton-Raphson approximation.
Use double calculations for the initial value with a fallback to pow(x, 1/n).

Pow throws Overflow in special case

The next code throws Overflow error
BigDecimalMath.pow(new BigDecimal("85"), new BigDecimal("483379540.5878915618046344614959831"), MathContext.DECIMAL128)

expected result
new BigDecimal("8.191399333915731143433650968385840E+932641633")

In attachment you can find the fix (Intellij IDEA patch file)
bugfix_pow_special_case_overflow.patch

Support OSGi

The MANIFEST.MF must contain the necessary OSGi headers

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.