Skip to content

Latest commit

 

History

History
223 lines (195 loc) · 17.7 KB

File metadata and controls

223 lines (195 loc) · 17.7 KB

Num

Maven Central Javadoc Codecov Java Version License

A Java library that abstracts the mathematical operations on real decimal numbers represented in computer memory as floating-point binary numbers (Double) or arbitrary-precision decimal numbers (BigDecimal).

Installation

For build.gradle.kts:

implementation("trade.invision:num:3.0.1")

For build.gradle:

implementation 'trade.invision:num:3.0.1'

For pom.xml:

<dependency>
    <groupId>trade.invision</groupId>
    <artifactId>num</artifactId>
    <version>3.0.1</version>
</dependency>

Motivation

IEEE 754 floating-point binary numbers cannot accurately represent non-integer real decimal numbers in computer memory and FPUs. In applications that require accurate mathematical operations with non-integer (fractional) decimal numbers, such as finance programs involving money transactions and currency, using arbitrary-precision decimal numbers is best practice. Using floating-point numbers (e.g. float or double) to represent currency can lead to undesirable inequality calculations, rounding errors, and precision loss. Thankfully, Java provides the BigDecimal class for working with arbitrary-precision decimal numbers, though it has some quirks and doesn't provide many of the functions that the Math class has for float and double types. Additionally, mathematical operations on BigDecimal objects can be significantly slower than the equivalent mathematical operations on floating-point numbers. This can be problematic in applications such as quantitative finance and algorithmic trading programs where fast execution time of mathematical operations on currency values is more desirable and the tradeoff of worse accuracy and lower precision is worth it. Using integer types as a fixed-point number to represent a currency's minor unit is another approach, but it can be unintuitive to work with and is still subject to precision loss. The side effects of using floating-point numbers for currency may be negligible in certain contexts, such as working with relatively low precision numbers (like stock prices) or measurements (like technical indicators). So, using floating-point numbers for currency values isn't always a bad idea. Even Microsoft Excel, a program widely used in finance, uses floating-point numbers. You can really go back-and-forth on the tradeoffs between floating-point, fixed-point, and arbitrary-precision numbers. Enter, the Num interface: an intuitive interface that allows you to focus on using currency values in your application without constantly worrying about the underlying number representation and easily switch between various number representations at runtime.

The Num Interface

Num, short for "number", is an interface for performing mathematical operations on real decimal numbers. Implementations wrap a Number instance so that performing mathematical operations on floating-point binary numbers (Double via DoubleNum) or arbitrary-precision decimal numbers (BigDecimal via DecimalNum) is simple. All implementations of this interface are interoperable with each other. Operations involving different implementations will result in a Num that trends towards an increase in precision. For example, subtracting a DecimalNum from a DoubleNum will result in a DecimalNum. For another example, subtracting a DecimalNum with a context precision of 16 from a DecimalNum with a context precision of 32 will result in a DecimalNum with a context precision of 32. Mathematical operations that result in NaN, +Infinity, -Infinity, or throw an ArithmeticException will yield a NaNNum.

Usage

To get a DoubleNum instance, provide a Number (byte, short, int, long, float, double, BigDecimal), String, or existing Num to the DoubleNum.doubleNum() static method. Statically importing doubleNum() is preferred as your code will likely look cleaner.

Getting a DecimalNum instance is similar to DoubleNum, but requires you to specify a precision and rounding mode via a MathContext. Use DecimalNum.decimalNum(String, MathContext) or use one of the convenience methods, such as decimalNum64() which provides approximately the same precision as double, allowing up to 16 significant figures of precision and the same rounding policy as double. Again, statically importing decimalNum() is preferred as your code will likely look cleaner.

The NumFactory interface makes it easy to get Num instances from a given Number, String, or existing Num by simply using NumFactory.of(...) instead of calling the DoubleNum or DecimalNum static methods directly. To get a NumFactory instance for DoubleNum, use DoubleNum.FACTORY. To get a NumFactory instance for DecimalNum, use one of the static methods such as DecimalNum.decimalNum64Factory().

Check out the Javadoc for all classes and method signatures, but here's a quick reference:

For Num:

For NumFactory:

Acknowledgement

This library's Num interface was inspired by the Num interface of the excellent ta4j library. There are several improvements and additions that this library's Num interface provides:

  • Interoperability between DoubleNum and DecimalNum
  • Several more mathematical operations (e.g. trigonometry functions) via Math in DoubleNum and via big-math in DecimalNum
  • No default precision for DecimalNum (see ta4j issue)
  • Configurable epsilon for tolerant comparison operations (see ta4j DoubleNum)
  • Number used instead of primitive overloads
  • Documentation improvements

Big thanks to Eric Obermühlner for the excellent big-math library.

Maintained by Invision

This project is maintained by Invision. Invision enables you to automate and test your investment and trading strategies across billions of data points in seconds using quantitative finance and algorithmic trading programs you build on our code or no-code platform.