mirror of
https://github.com/espocrm/espocrm.git
synced 2026-03-03 02:27:01 +00:00
bcmath and currency float casting fix
This commit is contained in:
@@ -33,7 +33,7 @@ use DivisionByZeroError;
|
||||
|
||||
class CalculatorUtil
|
||||
{
|
||||
private const SCALE = 14;
|
||||
private const int SCALE = 14;
|
||||
|
||||
/**
|
||||
* @param numeric-string $arg1
|
||||
@@ -43,16 +43,10 @@ class CalculatorUtil
|
||||
public static function add(string $arg1, string $arg2): string
|
||||
{
|
||||
if (!function_exists('bcadd')) {
|
||||
return (string) (
|
||||
(float) $arg1 + (float) $arg2
|
||||
);
|
||||
return self::floatToString((float) $arg1 + (float) $arg2);
|
||||
}
|
||||
|
||||
return bcadd(
|
||||
$arg1,
|
||||
$arg2,
|
||||
self::SCALE
|
||||
);
|
||||
return bcadd($arg1, $arg2, self::SCALE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -63,56 +57,42 @@ class CalculatorUtil
|
||||
public static function subtract(string $arg1, string $arg2): string
|
||||
{
|
||||
if (!function_exists('bcsub')) {
|
||||
return (string) (
|
||||
(float) $arg1 - (float) $arg2
|
||||
);
|
||||
return self::floatToString((float) $arg1 - (float) $arg2);
|
||||
}
|
||||
|
||||
return bcsub(
|
||||
$arg1,
|
||||
$arg2,
|
||||
self::SCALE
|
||||
);
|
||||
return bcsub($arg1, $arg2, self::SCALE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param numeric-string $arg1
|
||||
* @param numeric-string $arg2
|
||||
* @return numeric-string
|
||||
*
|
||||
* @todo For the result, trim right zeros. Then, trim dot.
|
||||
*/
|
||||
public static function multiply(string $arg1, string $arg2): string
|
||||
{
|
||||
if (!function_exists('bcmul')) {
|
||||
return (string) (
|
||||
(float) $arg1 * (float) $arg2
|
||||
);
|
||||
return self::floatToString((float) $arg1 * (float) $arg2);
|
||||
}
|
||||
|
||||
return bcmul(
|
||||
$arg1,
|
||||
$arg2,
|
||||
self::SCALE
|
||||
);
|
||||
return bcmul($arg1, $arg2, self::SCALE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param numeric-string $arg1
|
||||
* @param numeric-string $arg2
|
||||
* @return numeric-string
|
||||
*
|
||||
* @todo For the result, trim right zeros. Then, trim dot.
|
||||
*/
|
||||
public static function divide(string $arg1, string $arg2): string
|
||||
{
|
||||
if (!function_exists('bcdiv')) {
|
||||
return (string) (
|
||||
(float) $arg1 / (float) $arg2
|
||||
);
|
||||
return self::floatToString((float) $arg1 / (float) $arg2);
|
||||
}
|
||||
|
||||
$result = bcdiv(
|
||||
$arg1,
|
||||
$arg2,
|
||||
self::SCALE
|
||||
);
|
||||
$result = bcdiv($arg1, $arg2, self::SCALE);
|
||||
|
||||
if ($result === null) { /** @phpstan-ignore-line */
|
||||
throw new DivisionByZeroError();
|
||||
@@ -128,7 +108,9 @@ class CalculatorUtil
|
||||
public static function round(string $arg, int $precision = 0): string
|
||||
{
|
||||
if (!function_exists('bcadd')) {
|
||||
return (string) round((float) $arg, $precision);
|
||||
return self::floatToString(
|
||||
round((float) $arg, $precision)
|
||||
);
|
||||
}
|
||||
|
||||
$addition = '0.' . str_repeat('0', $precision) . '5';
|
||||
@@ -139,11 +121,7 @@ class CalculatorUtil
|
||||
|
||||
assert(is_numeric($addition));
|
||||
|
||||
return bcadd(
|
||||
$arg,
|
||||
$addition,
|
||||
$precision
|
||||
);
|
||||
return bcadd($arg, $addition, $precision);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -156,10 +134,15 @@ class CalculatorUtil
|
||||
return (float) $arg1 <=> (float) $arg2;
|
||||
}
|
||||
|
||||
return bccomp(
|
||||
$arg1,
|
||||
$arg2,
|
||||
self::SCALE
|
||||
);
|
||||
return bccomp($arg1, $arg2, self::SCALE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return numeric-string
|
||||
*/
|
||||
private static function floatToString(float $amount): string
|
||||
{
|
||||
/** @var numeric-string */
|
||||
return rtrim(rtrim(sprintf('%.' . self::SCALE . 'f', $amount), '0'), '.');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,8 @@ use InvalidArgumentException;
|
||||
*/
|
||||
class Currency
|
||||
{
|
||||
private const int DEFAULT_SCALE = 14;
|
||||
|
||||
/** @var numeric-string */
|
||||
private string $amount;
|
||||
private string $code;
|
||||
@@ -57,8 +59,10 @@ class Currency
|
||||
throw new InvalidArgumentException("Bad currency code.");
|
||||
}
|
||||
|
||||
if (is_float($amount) || is_int($amount)) {
|
||||
if (is_int($amount)) {
|
||||
$amount = (string) $amount;
|
||||
} else if (is_float($amount)) {
|
||||
$amount = self::floatToString($amount);
|
||||
}
|
||||
|
||||
$this->amount = $amount;
|
||||
@@ -136,10 +140,9 @@ class Currency
|
||||
*/
|
||||
public function multiply(float|int|string $multiplier): self
|
||||
{
|
||||
$amount = CalculatorUtil::multiply(
|
||||
$this->getAmountAsString(),
|
||||
(string) $multiplier
|
||||
);
|
||||
$multiplier = is_float($multiplier) ? self::floatToString($multiplier) : (string) $multiplier;
|
||||
|
||||
$amount = CalculatorUtil::multiply($this->getAmountAsString(), $multiplier);
|
||||
|
||||
return new self($amount, $this->getCode());
|
||||
}
|
||||
@@ -151,10 +154,9 @@ class Currency
|
||||
*/
|
||||
public function divide(float|int|string $divider): self
|
||||
{
|
||||
$amount = CalculatorUtil::divide(
|
||||
$this->getAmountAsString(),
|
||||
(string) $divider
|
||||
);
|
||||
$divider = is_float($divider) ? self::floatToString($divider) : (string) $divider;
|
||||
|
||||
$amount = CalculatorUtil::divide($this->getAmountAsString(), $divider);
|
||||
|
||||
return new self($amount, $this->getCode());
|
||||
}
|
||||
@@ -208,4 +210,13 @@ class Currency
|
||||
{
|
||||
return new self($amount, $code);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return numeric-string
|
||||
*/
|
||||
private static function floatToString(float $amount): string
|
||||
{
|
||||
/** @var numeric-string */
|
||||
return rtrim(rtrim(sprintf('%.' . self::DEFAULT_SCALE . 'f', $amount), '0'), '.');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ use InvalidArgumentException;
|
||||
|
||||
class CurrencyTest extends TestCase
|
||||
{
|
||||
public function testValue()
|
||||
public function testValue1()
|
||||
{
|
||||
$value = Currency::create(2.0, 'USD');
|
||||
|
||||
@@ -47,6 +47,14 @@ class CurrencyTest extends TestCase
|
||||
$this->assertEquals('USD', $value->getCode());
|
||||
}
|
||||
|
||||
public function testValue()
|
||||
{
|
||||
$value = Currency::create(0.5, 'USD');
|
||||
|
||||
$this->assertEquals('0.5', $value->getAmountAsString());
|
||||
$this->assertEquals('USD', $value->getCode());
|
||||
}
|
||||
|
||||
public function testAdd()
|
||||
{
|
||||
$value = (new Currency(2.0, 'USD'))->add(
|
||||
@@ -67,7 +75,7 @@ class CurrencyTest extends TestCase
|
||||
$this->assertEquals('USD', $value->getCode());
|
||||
}
|
||||
|
||||
public function testMultiply()
|
||||
public function testMultiply1()
|
||||
{
|
||||
$value = (new Currency(2.0, 'USD'))->multiply(3.0);
|
||||
|
||||
@@ -75,7 +83,15 @@ class CurrencyTest extends TestCase
|
||||
$this->assertEquals('USD', $value->getCode());
|
||||
}
|
||||
|
||||
public function testDivide()
|
||||
public function testMultiply2()
|
||||
{
|
||||
$value = (new Currency(2.0, 'USD'))->multiply(0.5);
|
||||
|
||||
$this->assertEquals('1.00000000000000', $value->getAmountAsString());
|
||||
$this->assertEquals('USD', $value->getCode());
|
||||
}
|
||||
|
||||
public function testDivide1()
|
||||
{
|
||||
$value = (new Currency(6.0, 'USD'))->divide(3.0);
|
||||
|
||||
@@ -83,6 +99,14 @@ class CurrencyTest extends TestCase
|
||||
$this->assertEquals('USD', $value->getCode());
|
||||
}
|
||||
|
||||
public function testDivide2()
|
||||
{
|
||||
$value = (new Currency(6.0, 'USD'))->divide(0.5);
|
||||
|
||||
$this->assertEquals('12.00000000000000', $value->getAmount());
|
||||
$this->assertEquals('USD', $value->getCode());
|
||||
}
|
||||
|
||||
public function testRound1()
|
||||
{
|
||||
$value = (new Currency(2.306, 'USD'))->round(2);
|
||||
|
||||
Reference in New Issue
Block a user