# Arbitrary-precision decimal arithmetic for PHP 7

This library provides a PHP extension that adds support for correctly-rounded, arbitrary-precision decimal floating point arithmetic. Applications that rely on accurate numbers (ie. money, measurements, or mathematics) can use `Decimal` instead of `float` or `string` to represent numerical values.

The implementation uses the same arbitrary precision library as Python’s decimal, called mpdecimal.

The decimal extension offers several advantages over the `float` data type:

• All rational numbers can be represented accurately. In contrast, numbers like `0.2` do not have exact representations in binary floating point. You can read more about this in the floating point guide or in PHP’s documentation.
• While a binary floating point value like `0.2` comes close, the small difference prevents reliable equality testing, and inaccuracies may accumulate over time.
• Arbitrary precision allows for numbers that are not bound by the same upper and lower limits as `float` - numbers can be as big or small as required.
• PHP does a good job of hiding the inaccuracies of binary floating point representation with the precision INI setting. By default, `0.1 + 0.2` will have a string value of `0.3` even though the internal C `double` can not represent the result accurately. For example:
``````var_dump(0.1 + 0.2);        // float(0.3)
var_dump(0.1 + 0.2 - 0.3);  // float(5.5511151231258E-17)
``````

PHP already has arbitrary precision math functions…

The current goto answer for arbitrary precision math in PHP is bcmath. However, the `Decimal` class offers multiple advantages over bcmath:

• Decimal values are objects, so you can typehint `Decimal` instead of `string`.
• Arithmetic and comparison operators are supported.
• Precision is defined as the number of significant figures, and scale is the number of digits behind the decimal point. This means that a number like `1.23E-1000` would require a scale of `1002` but a precision of `3`. This library uses precision; bcmath uses scale.
• Scientific notation is supported, so you can use strings like `"1.23E-1000"` to construct a `Decimal`. At the time of this writing, you can not do this with bcmath.
• Calculations are significantly faster. See #performance for some benchmarks.

## Installation

#### Composer

Composer can not be used to install the extension. The `php-decimal/php-decimal` package can be used to specify the extension as a dependency and provides stubs for IDE integration. If you are using Composer and would like to add this extension as a dependency, require `php-decimal/php-decimal`.

#### Install

The easiest way to install the extension is to use PECL:

``````pecl install decimal
``````

If you are using phpbrew:

``````phpbrew ext install decimal
phpbrew ext enable  decimal
``````

#### Enable

Remember to enable to extension in your .ini file.

``````extension=decimal.so        # Unix, OS X
extension=php_decimal.dll   # Windows
``````

#### Verify

You can confirm that the extension is installed with `php --re decimal`.

## Basic Usage

The `Decimal` class is under the `Decimal` namespace.

`Decimal` objects can be constructed using a `Decimal`, `string`, or `int` value, and an optional precision which defaults to 28.

Special `float` values are also supported (`NAN`, `INF` and `-INF`), but `float` is otherwise not a valid argument in order to avoid accidentially using a `float`. If you absolutely must use a `float` to construct a decimal you can cast it to a `string` first, but doing so if affected by the precision INI setting.

Projects using this extension should avoid `float` entirely, wherever possible. An example workflow is to store values as `DECIMAL` in the database, query them as `string`, parse to `Decimal`, perform calculations, and finally prepare for the database using toFixed.

JSON conversions will automatically convert the decimal to `string` using all signficant figures.

A warning will be raised if value was not parsed completely. For example, `"0.135"` to a precision of `2` will result in `"0.14"` with a warning. Similarly, `123` with a precision of `2` would result in `120` with a warning because data has been lost.

`Decimal` is final and immutable. Arithmetic operations always return a new `Decimal` using the maximum precision of the object and the operands. The result is therefore accurate up to `MAX(\$this->precision(), \$op1->precision(), ...)` significant figures, subject to rounding of the last digit.

For example:

``````use Decimal\Decimal;

\$a = new Decimal("1", 2);
\$b = new Decimal("7", 8);

print_r(\$a / \$b);
``````
``````Decimal\Decimal Object
(
[value] => 0.14285714
[precision] => 8
)
``````

Scalar operands inherit the precision of the `Decimal` operand, which avoids the need to construct a new object for the operation. If a scalar operand must be parsed with a higher precision, you should construct a new `Decimal` with an explicit precision. The result of a decimal operation is always a `Decimal`.

For example:

``````use Decimal\Decimal;

\$op1 = new Decimal("0.1", 4);
\$op2 = "0.123456789";

print_r(\$op1 + \$op2);

use Decimal\Decimal;

/**
* @param int \$n The factorial to calculate, ie. \$n!
* @param int \$p The precision to calculate the factorial to.
*
* @return Decimal
*/
function factorial(int \$n, int \$p = Decimal::DEFAULT_PRECISION): Decimal
{
return \$n < 2 ? new Decimal(\$n, \$p) : \$n * factorial(\$n - 1, \$p);
}

echo factorial(10000, 32);
``````
``````Warning: Loss of data on string conversion in ... on line 1
Decimal\Decimal Object
(
[value] => 0.2235
[precision] => 4
)
``````

## Sandbox

This is a limited environment where you can experiment with `Decimal`.

<?php use Decimal\Decimal; \$a = new Decimal("0.1"); \$b = new Decimal("7.0"); echo \$a / \$b;

## Performance

The benchmark performs calculations on `string` and `int` values, then converts the result to a string at the very end. While this does not represent cases where a single operation is performed and exported, the goal is to simulate a realistic workflow where a number is created, used in a few calculations, and exported at the end.

It is difficult to determine what to use for the scale of bcmath, because it specifies the number of places behind the decimal point, rather than the precision, which is the total number of significant places. This benchmark is therefore arbitrary in itself and serves only to provide a rough idea of what to expect.

The code for this basic benchmark can be found here.

Results are the total runtime to produce a result across many iterations, in seconds. Lower is better.

#### Results

`bcmath` `string` `3.5520` `3.6620` `6.7272` `25.8195`
`php-decimal` `string` `2.5652` `2.6048` `2.5794` `5.32710`
`bcmath` `int` `4.2136` `4.2002` `5.5506` `11.5603`
`php-decimal` `int` `1.6846` `1.6523` `1.7213` `4.7780`

## Attributes

### Precision

Precision defines the number of significant figures that a decimal is accurate to. For example, a number like `1.23E-200` is very small but only has 3 significant figures. PHP decimal uses a default precision of `28` and does not take the precision setting in the .ini into account (which is for converting `float` to string). Increasing the precision will require more memory and might impact runtime significantly for operations like `pow` and `div` when using a very high precision.

Decimal operations, casting and construction will always preserve precision to avoid data loss:

• You can’t reduce the precision of a decimal.
• You can’t change the value of a decimal after it has been calculated.
• Constructing a decimal using a decimal will preserve the given decimal’s precision.

For example:

``````\$a = new Decimal("0.1", 50);

\$b = new Decimal(\$a);     // Precision is 50
\$c = new Decimal(\$b, 6);  // Precision is 50
\$d = new Decimal(\$c, 64); // Precision is 64
``````

Arithmetic operations will result in a new decimal using the maximum precision of all operands. The developer’s only responsibility is to define the precision (as a minimum) when constructing a decimal. For example, if you have a `DECIMAL(20,6)` column in your database (precision is 20, scale is 6), you would create a decimal instance using 20 for the precision and be assured that all calculations will use and result in a precision of at least 20. When the value is to be written back to the database, you would use `\$decimal->toFixed(6)` to produce a string rounded accurately to 6 decimal places to match the scale of the SQL data type.

There are three precision constants:

• `Decimal::MIN_PRECISION`
• `Decimal::MAX_PRECISION`
• `Decimal::DEFAULT_PRECISION`
->precision(): int
Returns:
int, the precision of this decimal.

### Special Numbers

There are 3 special numbers: `INF`, `-INF` and `NAN`. These correspond to the same `float` value constants in PHP. All comparison and arithmetic using these values match the behaviour of PHP `float` values wherever possible, and any case that does not do so is considered a bug.

->isNaN(): bool
Returns:
bool, `TRUE` if this decimal is not a defined number.
->isInf(): bool
Returns:
bool, `TRUE` if this decimal represents infinity, `FALSE` otherwise.

### Integers

->isInteger(): bool
Returns:
bool, `TRUE` if this decimal is an integer, ie. does not have significant figures behind the decimal point, otherwise `FALSE`.
->isZero(): bool
Returns:
bool, `TRUE` if this decimal is either positive or negative zero.

### Sign

->abs(): Decimal
Returns:
Decimal, the absolute (positive) value of this decimal.
->negate(): Decimal
Returns:
->signum(): int
Returns:
int, `0` if zero, `-1` if negative, or `1` if positive.
->isPositive(): bool
Returns:
bool, `TRUE` if this decimal is positive, `FALSE` otherwise.
->isNegative(): bool
Returns:
bool, `TRUE` if this decimal is negative, `FALSE` otherwise.

### Parity

->parity(): int
Returns:
int, `0` if the integer value of this decimal is even, `1` if odd. Special numbers like `NAN` and `INF` will return `1`.
->isEven(): bool
Returns:
bool, `TRUE` if this decimal is an integer and even, `FALSE` otherwise.
->isOdd(): bool
Returns:
bool, `TRUE` if this decimal is an integer and odd, `FALSE` otherwise.

## Rounding

The default rounding mode defined as `Decimal::DEFAULT_ROUNDING` is half-even, which is also the default used by IEEE 754, C#, Java, and Python. However, Javascript uses half-up, and both Ruby and PHP use half-away-from-zero.

The reason for this default is to prevent biasing the average upwards or downwards.

### Rounding Modes

The default rounding mode can not be changed because it affects how values are reduced to a precision. With a fixed internal rounding mode, an input value will always result in the same decimal value for a given precision, regardless of the environment. However, some methods allow you to provide a rounding mode, which can be any of the following constants:

• `Decimal::ROUND_UP` (away from from)
• `Decimal::ROUND_DOWN` (towards zero)
• `Decimal::ROUND_CEILING` (towards positive infinity)
• `Decimal::ROUND_FLOOR` (towards negative infinity)
• `Decimal::ROUND_HALF_UP` (halfway ties away from zero)
• `Decimal::ROUND_HALF_DOWN` (halfway ties towards zero)
• `Decimal::ROUND_HALF_EVEN` (halfway ties to nearest even number)
• `Decimal::ROUND_HALF_ODD` (halfway ties to nearest odd number)
• `Decimal::ROUND_TRUNCATE` (no rounding)

You can also use the corresponding PHP constants.

### Rounding Methods

->floor(): Decimal
Returns:
Decimal, the closest integer towards negative infinity.
->ceil(): Decimal
Returns:
Decimal, the closest integer towards positive infinity.
->truncate(): Decimal
Returns:
Decimal, the result of discarding all digits behind the decimal point.
->round(int \$places = 0int \$mode = Decimal::ROUND_HALF_EVEN): Decimal
Returns:
Decimal, the value of this decimal with the same precision, rounded according to the specified number of decimal places and rounding mode.
Throws:
• `InvalidArgumentException` if the rounding mode is not supported.
->trim(): Decimal
Returns:
Decimal, a copy of this decimal without trailing zeroes.
Since:

1.1.0

->toFixed(int \$places = 0bool \$commas = falseint \$mode = Decimal::ROUND_HALF_EVEN): string
Returns:
string, the value of this decimal formatted to a fixed number of decimal places, with thousands comma-separated, using a given rounding mode.

## Comparing

Decimal objects are equal if their numeric values are equal, as well as their precision. The only value that breaks this rule is `NAN`, which is not equal to anything else including itself. Precision is used as the tie-break in cases where the values are equal.

Decimal objects can be compared to any other type to determine equality or relative ordering. Non-decimal values will be converted to decimal first (using the maximum precision). In cases where the type is not supported or comparison is not defined (eg. a decimal compared to `"abc"`), the decimal is considered greater and an exception will not be thrown.

While decimal objects can not be constructed from a non-special `float`, they can be compared to `float`. This is done by implicitly converting the value to a string using the equivalent of a string cast. This conversion is affected by the .ini “precision” setting because an implicit cast should have the same behaviour as an explicit cast.

Decimal objects follow the standard PHP object conventions:

• Always `true`.
• Always greater than `NULL`.
• Identical (`===`) if they are the same object, even if equal.

There are two methods that you can use to compare:

->equals(mixed \$other): bool

This method is equivalent to the `==` operator.

Returns:
bool, `TRUE` if this decimal is considered equal to the given value.
->compareTo(mixed \$other): int

This method is equivalent to the `<=>` operator.

Returns:
int, ` 0` if this decimal is considered equal to `\$other`,
`-1` if this decimal should be placed before `\$other`,
` 1` if this decimal should be placed after `\$other`.

### Operators

Method Operators Description
`compareTo` `<=>`, `<`, `<=`, `>`, `>=` Relative ordering, sorting.
`equals` `==` Equality, equal precision.
`===` Identity, same exact object, even if equal.

## Converting

Decimal objects can be converted to `string`, `int`, and `float`.

->toInt(): int

This method is equivalent to a cast to int.

Returns:
int, the integer value of this decimal.
Throws:
• `OverflowException` if the value is greater than `PHP_INT_MAX`.
->toFloat(): float

This method is equivalent to a cast to float, and is not affected by the 'precision' INI setting.

Returns:
float, the native PHP floating point value of this decimal.
Throws:
• `OverflowException` if the value is greater than `PHP_FLOAT_MAX`.
• `UnderflowException` if the value is smaller than `PHP_FLOAT_MIN`.
->toString(): string

This method is equivalent to a cast to string.

Returns:
string, the value of this decimal represented exactly, in either fixed or scientific form, depending on the value.

### Casting

You can also cast a decimal to `string`, `float`, `int` and `bool`.

``````use Decimal\Decimal;

(bool)   new Decimal();      // true, by convention
(bool)   new Decimal(1);     // true
(int)    new Decimal("1.5"); // 1
(float)  new Decimal("1.5"); // 1.5
(string) new Decimal("1.5"); // 1.5
``````

Important: `(string)` or `toString` should not be used to produce a canonical representation of a decimal, because there is more than one way to represent the same value, and precision is not represented by the value itself. However, it is guaranteed that the string representation of a decimal can be used to construct a new decimal with the exact same value, assuming equal precision. If you want to store a decimal with its precision, you should use `serialize` and `unserialize`.

## Arithmetic

### Methods

This method is equivalent to the `+` operator.

Returns:
Decimal, the result of adding this decimal to the given value.
Throws:
• `TypeError` if the value is not a decimal, string or integer.
->sub(Decimal|string|int \$value): Decimal

This method is equivalent to the `-` operator.

Returns:
Decimal, the result of subtracting a given value from this decimal.
Throws:
• `TypeError` if the value is not a decimal, string or integer.
->mul(Decimal|string|int \$value): Decimal

This method is equivalent to the `*` operator.

Returns:
Decimal, the result of multiplying this decimal by the given value.
Throws:
• `TypeError` if the given value is not a decimal, string or integer.
->div(Decimal|string|int \$value): Decimal

This method is equivalent to the `/` operator.

Returns:
Decimal, the result of dividing this decimal by the given value.
Throws:
• `TypeError` if the value is not a decimal, string or integer.
• `DivisionByZeroError` if dividing by zero.
• `ArithmeticError` if division is undefined, eg. `INF` / -`INF`
->mod(Decimal|string|int \$value): Decimal

This method is equivalent to the `%` operator.

Returns:
Decimal, the remainder after dividing the integer value of this decimal by the integer value of the given value.
Throws:
• `TypeError` if the value is not a decimal, string or integer.
• `DivisionByZeroError` if the integer value of \$value is zero.
• `ArithmeticError` if the operation is undefined, eg. `INF` % -`INF`
->rem(Decimal|string|int \$value): Decimal

Returns:
Decimal, the remainder after dividing this decimal by a given value.
Throws:
• `TypeError` if the value is not a decimal, string or integer.
• `DivisionByZeroError` if the integer value of \$value is zero.
• `ArithmeticError` if the operation is undefined, eg. `INF`, -`INF`
->pow(Decimal|string|int \$exponent): Decimal

This method is equivalent to the `**` operator.

Returns:
Decimal, the result of raising this decimal to a given power.
Throws:
• `TypeError` if the exponent is not a decimal, string or integer.
->shift(int \$places): Decimal
Returns:
Decimal, A copy of this decimal with its decimal place shifted.
->ln(): Decimal

This method is equivalent in function to PHP's `log`.

Returns:
Decimal, the natural logarithm of this decimal (log base e), with the same precision as this decimal.
->exp(): Decimal
Returns:
Decimal, the exponent of this decimal, ie. e to the power of this, with the same precision as this decimal.
->log10(): Decimal
Returns:
Decimal, the base-10 logarithm of this decimal, with the same precision as this decimal.
->sqrt(): Decimal
Returns:
Decimal, the square root of this decimal, with the same precision as this decimal.
Decimal::sum(array|Traversable \$valuesint \$precision = 28): Decimal

The precision of the result will be the max of all precisions that were encountered during the calculation. The given precision should therefore be considered the minimum precision of the result. This method is equivalent to adding each value individually.

Returns:
Decimal, the sum of all given values.
Decimal::avg(array|Traversable \$valuesint \$precision = 28): Decimal

The precision of the result will be the max of all precisions that were encountered during the calculation. The given precision should therefore be considered the minimum precision of the result. This method is equivalent to adding each value individually, then dividing by the number of values.

Returns:
Decimal, the average of all given values.