Write a php function to round a float away from zero to a specified number of decimal places

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

Dependencies

  • PHP 7
  • libmpdec 2.4+

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:

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  0.2235
    [precision] => 4
]

Sandbox

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

Bài mới nhất

Chủ Đề