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 0 instead of 1 or 2 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 1 data type:
The current goto answer for arbitrary precision math in PHP is bcmath. However, the 0 class offers multiple advantages over bcmath:
InstallationDependencies
ComposerComposer can not be used to install the extension. The 8 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 8.InstallThe easiest way to install the extension is to use PECL:
If you are using phpbrew:
EnableRemember to enable to extension in your .ini file.
VerifyYou can confirm that the extension is installed with 0.Basic UsageThe 0 class is under the 0 namespace. 0 objects can be constructed using a 0, 2, or 6 value, and an optional precision which defaults to 28.Special 1 values are also supported ( 8, 9 and 0), but 1 is otherwise not a valid argument in order to avoid accidentially using a 1. If you absolutely must use a 1 to construct a decimal you can cast it to a 2 first, but doing so if affected by the INI setting.Projects using this extension should avoid 1 entirely, wherever possible. An example workflow is to store values as 6 in the database, query them as 2, parse to 0, perform calculations, and finally prepare for the database using .JSON conversions will automatically convert the decimal to 2 using all signficant figures.A warning will be raised if value was not parsed completely. For example, 0 to a precision of 1 will result in 2 with a warning. Similarly, 3 with a precision of 1 would result in 5 with a warning because data has been lost. 0 is final and immutable. Arithmetic operations always return a new 0 using the maximum precision of the object and the operands. The result is therefore accurate up to 8 significant figures, subject to rounding of the last digit.For example:
Scalar operands inherit the precision of the 0 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 0 with an explicit precision. The result of a decimal operation is always a 0.For example:
SandboxThis is a limited environment where you can experiment with 0.<?php use Decimal\Decimal; $a = new Decimal("0.1"); $b = new Decimal("7.0"); echo $a / $b; Run Example Reset PerformanceThe benchmark performs calculations on 2 and 6 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. ResultsTypeAddSubtractMultiplyDivide 5 2 7 8 9 0 1 2 3 4 5 6 5 6 9 0 1 2 1 6 5 6 7 8AttributesPrecisionPrecision defines the number of significant figures that a decimal is accurate to. For example, a number like 9 is very small but only has 3 significant figures. PHP decimal uses a default precision of 0 and does not take the precision setting in the .ini into account (which is for converting 1 to string). Increasing the precision will require more memory and might impact runtime significantly for operations like 2 and 3 when using a very high precision.Decimal operations, casting and construction will always preserve precision to avoid data loss:
For example:
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 4 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 5 to produce a string rounded accurately to 6 decimal places to match the scale of the SQL data type.There are three precision constants:
->precision(): int Returns: int, the precision of this decimal. Special NumbersThere are 3 special numbers: 9, 0 and 8. These correspond to the same 1 value constants in PHP. All comparison and arithmetic using these values match the behaviour of PHP 1 values wherever possible, and any case that does not do so is considered a bug.->isNaN(): bool Returns: bool, ->isInf(): bool Returns: bool, Integers->isInteger(): bool Returns: bool, ->isZero(): bool Returns: bool, Sign->abs(): Decimal Returns: Decimal, the absolute (positive) value of this decimal. ->negate(): Decimal Returns: Decimal, the same value as this decimal, but the sign inverted. ->signum(): int Returns: int, ->isPositive(): bool Returns: bool, ->isNegative(): bool Returns: bool, Parity->parity(): int Returns: int, ->isEven(): bool Returns: bool, ->isOdd(): bool Returns: bool, RoundingThe default rounding mode defined as 26 is , which is also the default used by IEEE 754, , Java, and . However, uses , and both and PHP use .The reason for this default is to prevent biasing the average upwards or downwards. This stack exchange answer provides some great examples for further reading. Rounding ModesThe 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:
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 = 0, int $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:
->trim(): Decimal Returns: Decimal, a copy of this decimal without trailing zeroes. Since: 1.1.0 ->toFixed(int $places = 0, bool $commas = false, int $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. ComparingDecimal objects are equal if their numeric values are equal, as well as their precision. The only value that breaks this rule is 8, 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 38), the decimal is considered greater and an exception will not be thrown.While decimal objects can not be constructed from a non-special 1, they can be compared to 1. 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:
There are two methods that you can use to compare: ->equals(mixed $other): bool This method is equivalent to the 44 operator.Returns: bool, ->compareTo(mixed $other): int This method is equivalent to the 46 operator.Returns: int, OperatorsMethodOperatorsDescription 53 46, 55, 56, 57, 58Relative ordering, sorting. 59 44Equality, equal precision. 43Identity, same exact object, even if equal.ConvertingDecimal objects can be converted to 2, 6, and 1.->toInt(): int This method is equivalent to a cast to int. Returns: int, the integer value of this decimal. Throws:
->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:
->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. CastingYou can also cast a decimal to 2, 1, 6 and 74.
Important: 75 or 76 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 77 and 78.ArithmeticMethods->add(Decimal|string|int $value): Decimal This method is equivalent to the 79 operator.Returns: Decimal, the result of adding this decimal to the given value. Throws:
->sub(Decimal|string|int $value): Decimal This method is equivalent to the 81 operator.Returns: Decimal, the result of subtracting a given value from this decimal. Throws:
->mul(Decimal|string|int $value): Decimal This method is equivalent to the 83 operator.Returns: Decimal, the result of multiplying this decimal by the given value. Throws:
->div(Decimal|string|int $value): Decimal This method is equivalent to the 85 operator.Returns: Decimal, the result of dividing this decimal by the given value. Throws:
->mod(Decimal|string|int $value): Decimal This method is equivalent to the 91 operator.Returns: Decimal, the remainder after dividing the integer value of this decimal by the integer value of the given value. Throws:
->rem(Decimal|string|int $value): Decimal Returns: Decimal, the remainder after dividing this decimal by a given value. Throws:
->pow(Decimal|string|int $exponent): Decimal This method is equivalent to the 02 operator.Returns: Decimal, the result of raising this decimal to a given power. Throws:
->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 04.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 $values, int $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. |