| [ Web Proxy ] |
| Viewing: https://php-decimal.github.io/#basic-usage | [Back] [Original] |
[Github]
[Build Status]
[Build Status]
[PECL]
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 Pythons decimal, called mpdecimal.
The decimal extension offers several advantages over the float data type:
0.2 do not have exact representations in binary floating point. You can read more about this in the floating point guide or in PHPs documentation.0.2 comes close, the small difference prevents reliable equality testing, and inaccuracies may accumulate over time.float - numbers can be as big or small as required.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 instead of string.1.23E-1000 would require a scale of 1002 but a precision of 3. This library uses precision; bcmath uses scale."1.23E-1000" to construct a Decimal. At the time of this writing, you can not do this with bcmath.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.
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
Remember to enable to extension in your .ini file.
extension=decimal.so # Unix, OS X
extension=php_decimal.dll # Windows
You can confirm that the extension is installed with php --re decimal.
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
)
This is a limited environment where you can experiment with Decimal.
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.
| Type | Add | Subtract | Multiply | Divide | |
|---|---|---|---|---|---|
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 |
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:
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 developers 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_PRECISIONDecimal::MAX_PRECISIONDecimal::DEFAULT_PRECISIONThere 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.
TRUE if this decimal is not a defined number.
TRUE if this decimal represents infinity, FALSE otherwise.
TRUE if this decimal is an integer, ie. does not have significant figures behind the decimal point, otherwise FALSE.
TRUE if this decimal is either positive or negative zero.
0 if zero, -1 if negative, or 1 if positive.
TRUE if this decimal is positive, FALSE otherwise.
TRUE if this decimal is negative, FALSE otherwise.
0 if the integer value of this decimal is even, 1 if odd. Special numbers like NAN and INF will return 1.
TRUE if this decimal is an integer and even, FALSE otherwise.
TRUE if this decimal is an integer and odd, FALSE otherwise.
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.
This stack exchange answer provides some great examples for further reading.
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 zero)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.
InvalidArgumentException
if the rounding mode is not supported.
1.1.0
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:
true.NULL.===) if they are the same object, even if equal.There are two methods that you can use to compare:
This method is equivalent to the == operator.
TRUE if this decimal is considered equal to the given value.
This method is equivalent to the <=> operator.
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.
| Method | Operators | Description |
|---|---|---|
compareTo |
<=>, <, <=, >, >= |
Relative ordering, sorting. |
equals |
== |
Equality, equal precision. |
=== |
Identity, same exact object, even if equal. |
Decimal objects can be converted to string, int, and float.
This method is equivalent to a cast to int.
OverflowException
if the value is greater than PHP_INT_MAX.
This method is equivalent to a cast to float, and is not affected by the 'precision' INI setting.
OverflowException
if the value is greater than PHP_FLOAT_MAX.
UnderflowException
if the value is smaller than PHP_FLOAT_MIN.
This method is equivalent to a cast to string.
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.
This method is equivalent to the + operator.
TypeError
if the value is not a decimal, string or integer.
This method is equivalent to the - operator.
TypeError
if the value is not a decimal, string or integer.
This method is equivalent to the * operator.
TypeError
if the given value is not a decimal, string or integer.
This method is equivalent to the / operator.
TypeError
if the value is not a decimal, string or integer.
DivisionByZeroError
if dividing by zero.
ArithmeticError
if division is undefined, eg. INF / -INF
This method is equivalent to the % operator.
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
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
This method is equivalent to the ** operator.
TypeError
if the exponent is not a decimal, string or integer.
This method is equivalent in function to PHP's log.
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.
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.
| Web Proxy Viewer | New URL | Original Page |