Skip to content

Commit

Permalink
feat: 增加自动处理舍位和向上
Browse files Browse the repository at this point in the history
  • Loading branch information
sunjiehuan committed Oct 23, 2020
1 parent 828f04f commit ec7c082
Show file tree
Hide file tree
Showing 8 changed files with 71 additions and 47 deletions.
3 changes: 3 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,8 @@
},
"require-dev": {
"phpunit/phpunit": "^6.5"
},
"require": {
"ext-bcmath": "*"
}
}
6 changes: 1 addition & 5 deletions src/BC.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,7 @@ public function __call($name, $arguments)
if (!isset($arguments[0])) {
return 0;
}
$bcs = BCS::create($arguments[0], [
'scale' => $this->scale,
'rounded' => $this->rounded,
'operateScale' => $this->operateScale,
]);
$bcs = BCS::create($arguments[0], $this->config);
unset($arguments[0]);
/** @var BCS $bcs */
$bcs = $bcs->$name(...$arguments);
Expand Down
2 changes: 1 addition & 1 deletion src/BCParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public function parse($formula, $values = [])
'[\+\-]', // 加法、减法
];
$operand = '(?:(?<=[^0-9\.,]|^)[+-])?[0-9\.,]+';
$opScale = $this->operateScale;
$opScale = $this->config['operateScale'];
while (preg_match('/\(([^\)\(]+)\)/', $formula, $singleFormula)) {
foreach ($operations as $operation) {
while (preg_match("/({$operand})({$operation})({$operand})/", $singleFormula[1], $matches)) {
Expand Down
6 changes: 3 additions & 3 deletions src/BCS.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public function __call($name, $arguments)
if ($bcName === 'bcmod') {
$this->result = call_user_func($bcName, $this->result, $number);
} else {
$this->result = call_user_func($bcName, $this->result, $number, $this->operateScale);
$this->result = call_user_func($bcName, $this->result, $number, $this->config['operateScale']);
}
}
return $this;
Expand All @@ -72,7 +72,7 @@ public function getResult()
*/
public function getSqrt()
{
return bcsqrt($this->result, $this->scale);
return bcsqrt($this->result, $this->config['scale']);
}

/**
Expand All @@ -82,7 +82,7 @@ public function getSqrt()
*/
public function compare($number)
{
return bccomp($this->result, $number, $this->scale);
return bccomp($this->result, $number, $this->config['scale']);
}

/**
Expand Down
22 changes: 14 additions & 8 deletions src/BCSummary.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,11 @@ public static function create($config = [])
*/
public function average($total, $proportions)
{
$sum = BC::create(['scale' => $this->operateScale])->add(...array_values($proportions));
if (BCS::create($sum, ['scale' => $this->operateScale])->isEqual(0)) {
// 操作过程中的替换精度为操作精度
$operateConfig = array_merge($this->config, ['scale' => $this->config['operateScale']]);

$sum = BC::create($operateConfig)->add(...array_values($proportions));
if (BCS::create($sum, $operateConfig)->isEqual(0)) {
return array_map(function () {
return 0;
}, $proportions);
Expand All @@ -31,10 +34,10 @@ public function average($total, $proportions)
$lastOne = array_slice($proportions, count($proportions) - 1, 1, true);
array_pop($proportions);
foreach ($proportions as $index => $proportion) {
$result[$index] = BCS::create($proportion, ['scale' => $this->scale])->div($sum)->mul($total)->getResult();
$result[$index] = BCS::create($proportion, $this->config)->div($sum)->mul($total)->getResult();
}
$sumBefore = BC::create(['scale' => $this->scale])->add(...array_values($result));
$result[key($lastOne)] = BCS::create($total, ['scale' => $this->scale, 'rounded' => $this->rounded])->sub($sumBefore)->getResult();
$sumBefore = BC::create($this->config)->add(...array_values($result));
$result[key($lastOne)] = BCS::create($total, $this->config)->sub($sumBefore)->getResult();
return $result;
}

Expand All @@ -47,9 +50,12 @@ public function average($total, $proportions)
*/
public function upgrade($old, $new, $multi = 1)
{
if (BCS::create($old, ['scale' => $this->operateScale])->isEqual(0)) {
return BCS::create($new, ['scale' => $this->operateScale])->isEqual(0) ? 0 : 1;
// 操作过程中的替换精度为操作精度
$operateConfig = array_merge($this->config, ['scale' => $this->config['operateScale']]);

if (BCS::create($old, $operateConfig)->isEqual(0)) {
return BCS::create($new, $this->config)->isEqual(0) ? 0 : 1;
}
return BCS::create($new, ['scale' => $this->scale])->sub($old)->div($old)->mul($multi)->getResult();
return BCS::create($new, $this->config)->sub($old)->div($old)->mul($multi)->getResult();
}
}
54 changes: 27 additions & 27 deletions src/BaseBC.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,47 +4,47 @@

abstract class BaseBC
{
/**
* 精度
* null 会取 ini 中的配置,否则为 0
* @see setScale()
* @see getScale()
* @var null
*/
public $scale = null;
/**
* 是否四舍五入
* @var bool
*/
public $rounded = true;
/**
* 操作过程中的计算精度
* @var int
*/
public $operateScale = 18;
public $config = [
'scale' => null, // 精度,为 null 会取 ini 中的配置,否则为 0
'operateScale' => 18, // 操作过程中的计算精度
'round' => false, // 是否四舍五入,当向上和舍位都为 false 时,默认为四舍五入
'ceil' => false, // 是否向上取数,当有小数位时精度末位向上取
'floor' => false, // 是否舍位,当有小数位时舍去精度之后的
];

public function __construct($config = [])
{
foreach ($config as $name => $value) {
$this->{$name} = $value;
$this->config = array_merge($this->config, $config);

if (!$this->config['ceil'] && !$this->config['floor']) {
$this->config['round'] = true;
}
if (is_null($this->scale)) {
$this->scale = intval(ini_get('bcmath.scale')) ?: 0;
if (is_null($this->config['scale'])) {
$this->config['scale'] = intval(ini_get('bcmath.scale')) ?: 0;
}
}

/**
* 获取对应精度的数值
* @param $number
* @return bool|float|string
* @return float|string
*/
public function getScaleNumber($number)
{
$scale = $this->scale;
if ($this->rounded) {
$scale = $this->config['scale'];
if ($this->config['round']) {
return round($number, $scale);
}
$scale += 1;
return substr(sprintf("%.{$scale}f", $number), 0, -1);
if ($this->config['ceil']) {
$ratio = pow(10, $scale);
$number = ceil(bcmul($number, $ratio, $this->config['operateScale']));
return bcdiv($number, $ratio, $scale);
}
if ($this->config['floor']) {
$scale += 1;
return substr(sprintf("%.{$scale}f", $number), 0, -1);
}

return $number;
}
}
20 changes: 18 additions & 2 deletions tests/BCSTest.php
Original file line number Diff line number Diff line change
@@ -1,15 +1,31 @@
<?php


use kriss\bcmath\BCS;
use PHPUnit\Framework\TestCase;

class BCSTest extends TestCase
{
public function testGetResult()
{
// 默认四舍五入
$result = BCS::create(1.5, ['scale' => 2])->add(1.2)->mul(2)->sub(1.5)->getResult();
$this->assertEquals(3.9, $result);

// 四舍五入
$result = BCS::create(1.35, ['scale' => 2, 'round' => true])->add(1.2)->mul(1.35)->getResult();
$this->assertEquals(3.44, $result);

// 向上保留
$result = BCS::create(1.35, ['scale' => 2, 'ceil' => true])->add(1.2)->mul(1.35)->getResult();
$this->assertEquals(3.45, $result);

// 舍位
$result = BCS::create(1.35, ['scale' => 2, 'floor' => true])->add(1.2)->mul(1.35)->add(0.002)->getResult();
$this->assertEquals(3.44, $result);

// 操作过程中精度保留
$result = BCS::create(1.352, ['scale' => 2, 'operateScale' => 2])->add(0.014)->add(0.005)->getResult();
$this->assertEquals(1.36, $result);
}

public function testIsEqual()
Expand Down Expand Up @@ -53,7 +69,7 @@ public function testMul()
$result = BCS::create(1.2, ['scale' => 2])->mul(2.37, 3.84, 8.4)->getResult();
$this->assertEquals(91.74, $result);

$result = BCS::create(1.2, ['scale' => 4, 'rounded' => false])->mul(2.37, 3.84, 8.4)->getResult();
$result = BCS::create(1.2, ['scale' => 4, 'floor' => true])->mul(2.37, 3.84, 8.4)->getResult();
$this->assertEquals(91.7360, $result);
}

Expand Down
5 changes: 4 additions & 1 deletion tests/BCSummaryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ class BCSummaryTest extends TestCase

public function testAverage()
{
// 小数
$result = BCSummary::create(['scale' => 3])->average(18.8, [2.2, 2.2, 4.4]);
$this->assertEquals([4.7, 4.7, 9.4], $result);
// 简单
$result = BCSummary::create(['scale' => 2])->average(100, [2, 3]);
$this->assertEquals([40, 60], $result);
Expand Down Expand Up @@ -50,7 +53,7 @@ public function testAverage()
$this->assertEquals([1.1, 2.2, 3.4], $result);

// 保留一位,最后多余的使用舍去法
$result = BCSummary::create(['scale' => 1, 'rounded' => false])->average(6.66, [1.5, 3, 4.5]);
$result = BCSummary::create(['scale' => 1, 'floor' => true])->average(6.66, [1.5, 3, 4.5]);
$this->assertEquals([1.1, 2.2, 3.3], $result);

// 总数为 0
Expand Down

0 comments on commit ec7c082

Please sign in to comment.