Skip to content

Commit

Permalink
Add BigDecimal::sqrt() method
Browse files Browse the repository at this point in the history
  • Loading branch information
BenMorel committed Dec 7, 2018
1 parent 9fce93c commit 41eeaed
Show file tree
Hide file tree
Showing 2 changed files with 276 additions and 1 deletion.
46 changes: 46 additions & 0 deletions src/BigDecimal.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use Brick\Math\Exception\DivisionByZeroException;
use Brick\Math\Exception\MathException;
use Brick\Math\Exception\NegativeNumberException;
use Brick\Math\Internal\Calculator;

/**
Expand Down Expand Up @@ -408,6 +409,51 @@ public function quotientAndRemainder($that) : array
return [$quotient, $remainder];
}

/**
* Returns an approximation to the square root of this number, rounded down to the given number of decimals.
*
* @param int $scale
*
* @return BigDecimal
*
* @throws \InvalidArgumentException If the scale is negative.
* @throws NegativeNumberException If this number is negative.
*/
public function sqrt(int $scale) : BigDecimal
{
if ($scale < 0) {
throw new \InvalidArgumentException('Scale cannot be negative.');
}

if ($this->value === '0') {
return new BigDecimal('0', $scale);
}

if ($this->value[0] === '-') {
throw new NegativeNumberException('Cannot calculate the square root of a negative number.');
}

$value = $this->value;
$addDigits = 2 * $scale - $this->scale;

if ($addDigits > 0) {
// add zeros
$value .= str_repeat('0', $addDigits);
} elseif ($addDigits < 0) {
// trim digits
if (-$addDigits >= strlen($this->value)) {
// requesting a scale too low, will always yield a zero result
return new BigDecimal('0', $scale);
}

$value = substr($value, 0, $addDigits);
}

$value = Calculator::get()->sqrt($value);

return new BigDecimal($value, $scale);
}

/**
* Returns a copy of this BigDecimal with the decimal point moved $n places to the left.
*
Expand Down
231 changes: 230 additions & 1 deletion tests/BigDecimalTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
namespace Brick\Math\Tests;

use Brick\Math\BigDecimal;
use Brick\Math\RoundingMode;
use Brick\Math\Exception\DivisionByZeroException;
use Brick\Math\Exception\NegativeNumberException;
use Brick\Math\Exception\RoundingNecessaryException;
use Brick\Math\RoundingMode;

/**
* Unit tests for class BigDecimal.
Expand Down Expand Up @@ -1376,6 +1377,234 @@ public function testQuotientAndRemainderOfZeroThrowsException()
BigDecimal::of(1.2)->quotientAndRemainder(0);
}

/**
* @dataProvider providerSqrt
*
* @param string $number
* @param int $scale
* @param string $sqrt
*/
public function testSqrt(string $number, int $scale, string $sqrt)
{
$number = BigDecimal::of($number);

$this->assertBigDecimalEquals($sqrt, $number->sqrt($scale));
}

/**
* @return array
*/
public function providerSqrt()
{
return [
['0', 0, '0'],
['0', 1, '0.0'],
['0', 2, '0.00'],
['0.9', 0, '0'],
['0.9', 1, '0.9'],
['0.9', 2, '0.94'],
['0.9', 20, '0.94868329805051379959'],

['1', 0, '1'],
['1', 1, '1.0'],
['1', 2, '1.00'],
['1.01', 0, '1'],
['1.01', 1, '1.0'],
['1.01', 2, '1.00'],
['1.01', 50, '1.00498756211208902702192649127595761869450234700263'],

['2', 0, '1'],
['2', 1, '1.4'],
['2', 2, '1.41'],
['2', 3, '1.414'],
['2.0', 10, '1.4142135623'],
['2.00', 100, '1.4142135623730950488016887242096980785696718753769480731766797379907324784621070388503875343276415727'],
['2.01', 100, '1.4177446878757825202955618542708577926112284524295925478183838620667251915680282359142910339946198902'],

['3', 0, '1'],
['3', 1, '1.7'],
['3', 2, '1.73'],
['3.0', 3, '1.732'],
['3.00', 100, '1.7320508075688772935274463415058723669428052538103806280558069794519330169088000370811461867572485756'],
['3.01', 100, '1.7349351572897472412324994276999816954904345949805056180301062018688462654791174593725963060697252989'],

['4', 0, '2'],
['4.0', 1, '2.0'],
['4.00', 2, '2.00'],
['4.000', 50, '2.00000000000000000000000000000000000000000000000000'],
['4.001', 50, '2.00024998437695281987761450010498155779765165614814'],

['8', 0, '2'],
['8', 1, '2.8'],
['8', 2, '2.82'],
['8', 3, '2.828'],
['8', 100, '2.8284271247461900976033774484193961571393437507538961463533594759814649569242140777007750686552831454'],

['9', 0, '3'],
['9', 1, '3.0'],
['9', 2, '3.00'],
['9.0', 3, '3.000'],
['9.00', 50, '3.00000000000000000000000000000000000000000000000000'],
['9.000000000001', 100, '3.0000000000001666666666666620370370370372942386831275541552354823973654295585021450670206100119695201'],

['15', 0, '3'],
['15', 1, '3.8'],
['15', 2, '3.87'],
['15', 3, '3.872'],
['15', 100, '3.8729833462074168851792653997823996108329217052915908265875737661134830919369790335192873768586735179'],

['16', 0, '4'],
['16', 1, '4.0'],
['16.0', 2, '4.00'],
['16.0', 50, '4.00000000000000000000000000000000000000000000000000'],
['16.9', 100, '4.1109609582188931315985616077625340938354216811227818749147563086303727702310096877475225408930903837'],

['24.000000', 0, '4'],
['24.000000', 1, '4.8'],
['24.000000', 100, '4.8989794855663561963945681494117827839318949613133402568653851345019207549146300530797188662092804696'],

['25.0', 0, '5'],
['25.0', 1, '5.0'],
['25.0', 2, '5.00'],
['25.0', 50, '5.00000000000000000000000000000000000000000000000000'],

['35.0', 0, '5'],
['35.0', 1, '5.9'],
['35.0', 2, '5.91'],
['35.0', 3, '5.916'],
['35.0', 4, '5.9160'],
['35.0', 5, '5.91607'],
['35.0', 100, '5.9160797830996160425673282915616170484155012307943403228797196691428224591056530367657525271831091780'],
['35.000000000000001', 100, '5.9160797830996161270827537644132741956957234470198942745396537100863774127246283998019188486148209315'],
['35.999999999999999999999999', 100, '5.9999999999999999999999999166666666666666666666666660879629629629629629629629549254115226337448559670'],

['36.00', 0, '6'],
['36.00', 1, '6.0'],
['36.00', 2, '6.00'],
['36.00', 3, '6.000'],
['36.00', 50, '6.00000000000000000000000000000000000000000000000000'],

['48.00', 0, '6'],
['48.00', 2, '6.92'],
['48.00', 10, '6.9282032302'],
['48.00', 100, '6.9282032302755091741097853660234894677712210152415225122232279178077320676352001483245847470289943027'],
['48.99', 100, '6.9992856778388464346356995151906110076016504604210370025102717611026824990288822856842902895079113686'],

['49.000', 0, '7'],
['49.000', 1, '7.0'],
['49.000', 2, '7.00'],
['49.000', 50, '7.00000000000000000000000000000000000000000000000000'],

['63.000', 0, '7'],
['63.000', 1, '7.9'],
['63.000', 50, '7.93725393319377177150484726091778127713077754924735'],
['63.000', 100, '7.9372539331937717715048472609177812771307775492473505411050033776032064696908508832811786594236308318'],
['63.999', 100, '7.9999374997558574676327405322784897796491608172719005229581557200716046333750586163480165729931946120'],

['64.000', 0, '8'],
['64.000', 1, '8.0'],
['64.000', 2, '8.00'],
['64.000', 3, '8.000'],
['64.000', 5, '8.00000'],
['64.000', 10, '8.0000000000'],
['64.001', 100, '8.0000624997558612823300065647321162325407871131077227756517693705917932138407362275702583154308502098'],

['80.0000', 0, '8'],
['80.0000', 1, '8.9'],
['80.0000', 2, '8.94'],
['80.0000', 3, '8.944'],
['80.0000', 100, '8.9442719099991587856366946749251049417624734384461028970835889816420837025512195976576576335151290998'],
['80.9999', 100, '8.9999944444427297657453970731875168384468609868424736666730189165386046474591090720552569708711327582'],

['81.0000', 0, '9'],
['81.0000', 1, '9.0'],
['81.0000', 2, '9.00'],
['81.0000', 3, '9.000'],
['81.0000', 100, '9.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'],
['81.0001', 100, '9.0000055555538408789733941603538253684017661110877201067416238610513773808272313229667905649560045446'],

['99.0000', 0, '9'],
['99.0000', 1, '9.9'],
['99.0000', 2, '9.94'],
['99.0000', 3, '9.949'],
['99.0000', 100, '9.9498743710661995473447982100120600517812656367680607911760464383494539278271315401265301973848719527'],
['99.9999', 100, '9.9999949999987499993749996093747265622949217138670565794807433154677543830170797681772155388691566433'],

['100.00000', 0, '10'],
['100.00000', 1, '10.0'],
['100.00000', 2, '10.00'],
['100.00000', 3, '10.000'],
['100.00000', 100, '10.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'],
['100.00001', 100, '10.0000004999999875000006249999609375027343747949218911132799407960075378325233467481612458396019939178'],

['536137214136734800142146901786039940282473271927911507640625', 100, '732213912826528310663262741625.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'],
['536137214136734800142146901787504368108126328549238033123875', 100, '732213912826528310663262741625.9999999999999999999999999999993171394434860226777473099041602996062918768042176806905944729779944325'],
['536137214136734800142146901787504368108126328549238033123876', 100, '732213912826528310663262741626.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'],
['5651495859544574019979802175954184725583245698990648064256.0000000001', 100, '75176431543034642899535752016.0000000000000000000000000000000000000006651020668808623891656648072197795077909627885735661691784990'],
['5651495859544574019979802176104537588669314984789719568288.9999999999', 100, '75176431543034642899535752016.9999999999999999999999999999999999999993348979331191376108343351927890677073964211143577833889761307'],
['5651495859544574019979802176104537588669314984789719568289.00000000001', 100, '75176431543034642899535752017.0000000000000000000000000000000000000000665102066880862389165664807210932292603578885642216611023869'],

['17', 60, '4.123105625617660549821409855974077025147199225373620434398633'],
['17', 61, '4.1231056256176605498214098559740770251471992253736204343986335'],
['17', 62, '4.12310562561766054982140985597407702514719922537362043439863357'],
['17', 63, '4.123105625617660549821409855974077025147199225373620434398633573'],
['17', 64, '4.1231056256176605498214098559740770251471992253736204343986335730'],
['17', 65, '4.12310562561766054982140985597407702514719922537362043439863357309'],
['17', 66, '4.123105625617660549821409855974077025147199225373620434398633573094'],
['17', 67, '4.1231056256176605498214098559740770251471992253736204343986335730949'],
['17', 68, '4.12310562561766054982140985597407702514719922537362043439863357309495'],
['17', 69, '4.123105625617660549821409855974077025147199225373620434398633573094954'],
['17', 70, '4.1231056256176605498214098559740770251471992253736204343986335730949543'],

['0.0019', 0, '0'],
['0.0019', 1, '0.0'],
['0.0019', 2, '0.04'],
['0.0019', 3, '0.043'],
['0.0019', 10, '0.0435889894'],
['0.0019', 70, '0.0435889894354067355223698198385961565913700392523244493689034413815955'],

['0.00000000015727468406479', 0, '0'],
['0.00000000015727468406479', 1, '0.0'],
['0.00000000015727468406479', 2, '0.00'],
['0.00000000015727468406479', 3, '0.000'],
['0.00000000015727468406479', 4, '0.0000'],
['0.00000000015727468406479', 5, '0.00001'],
['0.00000000015727468406479', 6, '0.000012'],
['0.00000000015727468406479', 7, '0.0000125'],
['0.00000000015727468406479', 8, '0.00001254'],
['0.00000000015727468406479', 9, '0.000012540'],
['0.00000000015727468406479', 10, '0.0000125409'],
['0.00000000015727468406479', 100, '0.0000125409203834802332262270521125445995500262491027973910117525063503841909945796984522050136239469'],

['0.04', 0, '0'],
['0.04', 1, '0.2'],
['0.04', 2, '0.20'],
['0.04', 10, '0.2000000000'],

['0.0004', 4, '0.0200'],
['0.00000000000000000000000000000004', 8, '0.00000000'],
['0.00000000000000000000000000000004', 16, '0.0000000000000002'],
['0.00000000000000000000000000000004', 32, '0.00000000000000020000000000000000'],
['0.000000000000000000000000000000004', 32, '0.00000000000000006324555320336758'],

['111111111111111111111.11111111111111', 90, '10540925533.894597773329645148109061726360556128277733889543457102096672435043305908711407747018689086']
];
}

public function testSqrtOfNegativeNumber()
{
$number = BigDecimal::of(-1);
$this->expectException(NegativeNumberException::class);
$number->sqrt(0);
}

public function testSqrtWithNegativeScale()
{
$number = BigDecimal::of(1);
$this->expectException(\InvalidArgumentException::class);
$number->sqrt(-1);
}

/**
* @dataProvider providerPower
*
Expand Down

0 comments on commit 41eeaed

Please sign in to comment.