Skip to content

Commit 67cdee6

Browse files
author
Mark Baker
authored
Add new Bitwise Functions introduced in MS Excel 2013 (#603)
* - Added calculation engine support for the new bitwise functions that were added in MS Excel 2013 - BITAND() Returns a Bitwise 'And' of two numbers - BITOR() Returns a Bitwise 'Or' of two number - BITXOR() Returns a Bitwise 'Exclusive Or' of two numbers - BITLSHIFT() Returns a number shifted left by a specified number of bits - BITRSHIFT() Returns a number shifted right by a specified number of bits
1 parent 9b44cf3 commit 67cdee6

File tree

10 files changed

+400
-2
lines changed

10 files changed

+400
-2
lines changed

CHANGELOG.md

+8-2
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1212
- Add excel function EXACT(value1, value2) support - [595](https://github.com/PHPOffice/PhpSpreadsheet/pull/595)
1313
- Support workbook view attributes for Xlsx format - [#523](https://github.com/PHPOffice/PhpSpreadsheet/issues/523)
1414
- Read and write hyperlink for drawing image - [#490](https://github.com/PHPOffice/PhpSpreadsheet/pull/490)
15-
- Fix ISFORMULA() function to work with a cell reference to another worksheet
16-
- Added calculation engine support for the new functions that were added in MS Excel 2013 and MS Excel 2016
15+
- Added calculation engine support for the new bitwise functions that were added in MS Excel 2013
16+
- BITAND() Returns a Bitwise 'And' of two numbers
17+
- BITOR() Returns a Bitwise 'Or' of two number
18+
- BITXOR() Returns a Bitwise 'Exclusive Or' of two numbers
19+
- BITLSHIFT() Returns a number shifted left by a specified number of bits
20+
- BITRSHIFT() Returns a number shifted right by a specified number of bits
21+
- Added calculation engine support for other new functions that were added in MS Excel 2013 and MS Excel 2016
1722
- Text Functions
1823
- CONCAT() Synonym for CONCATENATE()
1924
- NUMBERVALUE() Converts text to a number, in a locale-independent way
@@ -44,6 +49,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
4449

4550
### Fixed
4651

52+
- Fix ISFORMULA() function to work with a cell reference to another worksheet
4753
- Xlsx reader crashed when reading a file with workbook protection - [#553](https://github.com/PHPOffice/PhpSpreadsheet/pull/553)
4854
- Cell formats with escaped spaces were causing incorrect date formatting - [#557](https://github.com/PHPOffice/PhpSpreadsheet/issues/557)
4955
- Could not open CSV file containing HTML fragment - [#564](https://github.com/PHPOffice/PhpSpreadsheet/issues/564)

src/PhpSpreadsheet/Calculation/Calculation.php

+25
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,31 @@ class Calculation
369369
'functionCall' => [Statistical::class, 'BINOMDIST'],
370370
'argumentCount' => '4',
371371
],
372+
'BITAND' => [
373+
'category' => Category::CATEGORY_ENGINEERING,
374+
'functionCall' => [Engineering::class, 'BITAND'],
375+
'argumentCount' => '2',
376+
],
377+
'BITOR' => [
378+
'category' => Category::CATEGORY_ENGINEERING,
379+
'functionCall' => [Engineering::class, 'BITOR'],
380+
'argumentCount' => '2',
381+
],
382+
'BITXOR' => [
383+
'category' => Category::CATEGORY_ENGINEERING,
384+
'functionCall' => [Engineering::class, 'BITOR'],
385+
'argumentCount' => '2',
386+
],
387+
'BITLSHIFT' => [
388+
'category' => Category::CATEGORY_ENGINEERING,
389+
'functionCall' => [Engineering::class, 'BITLSHIFT'],
390+
'argumentCount' => '2',
391+
],
392+
'BITRSHIFT' => [
393+
'category' => Category::CATEGORY_ENGINEERING,
394+
'functionCall' => [Engineering::class, 'BITRSHIFT'],
395+
'argumentCount' => '2',
396+
],
372397
'CEILING' => [
373398
'category' => Category::CATEGORY_MATH_AND_TRIG,
374399
'functionCall' => [MathTrig::class, 'CEILING'],

src/PhpSpreadsheet/Calculation/Engineering.php

+173
Original file line numberDiff line numberDiff line change
@@ -2423,6 +2423,179 @@ public static function erfVal($x)
24232423
return self::$twoSqrtPi * $sum;
24242424
}
24252425

2426+
/**
2427+
* Validate arguments passed to the bitwise functions.
2428+
*
2429+
* @param mixed $value
2430+
*
2431+
* @throws Exception
2432+
*
2433+
* @return int
2434+
*/
2435+
private static function validateBitwiseArgument($value)
2436+
{
2437+
$value = Functions::flattenSingleValue($value);
2438+
2439+
if (is_int($value)) {
2440+
return $value;
2441+
} elseif (is_numeric($value)) {
2442+
if ($value == (int) ($value)) {
2443+
$value = (int) ($value);
2444+
if (($value > pow(2, 48) - 1) || ($value < 0)) {
2445+
throw new Exception(Functions::NAN());
2446+
}
2447+
2448+
return $value;
2449+
}
2450+
2451+
throw new Exception(Functions::NAN());
2452+
}
2453+
2454+
throw new Exception(Functions::VALUE());
2455+
}
2456+
2457+
/**
2458+
* BITAND.
2459+
*
2460+
* Returns the bitwise AND of two integer values.
2461+
*
2462+
* Excel Function:
2463+
* BITAND(number1, number2)
2464+
*
2465+
* @category Engineering Functions
2466+
*
2467+
* @param int $number1
2468+
* @param int $number2
2469+
*
2470+
* @return int|string
2471+
*/
2472+
public static function BITAND($number1, $number2)
2473+
{
2474+
try {
2475+
$number1 = self::validateBitwiseArgument($number1);
2476+
$number2 = self::validateBitwiseArgument($number2);
2477+
} catch (Exception $e) {
2478+
return $e->getMessage();
2479+
}
2480+
2481+
return $number1 & $number2;
2482+
}
2483+
2484+
/**
2485+
* BITOR.
2486+
*
2487+
* Returns the bitwise OR of two integer values.
2488+
*
2489+
* Excel Function:
2490+
* BITOR(number1, number2)
2491+
*
2492+
* @category Engineering Functions
2493+
*
2494+
* @param int $number1
2495+
* @param int $number2
2496+
*
2497+
* @return int|string
2498+
*/
2499+
public static function BITOR($number1, $number2)
2500+
{
2501+
try {
2502+
$number1 = self::validateBitwiseArgument($number1);
2503+
$number2 = self::validateBitwiseArgument($number2);
2504+
} catch (Exception $e) {
2505+
return $e->getMessage();
2506+
}
2507+
2508+
return $number1 | $number2;
2509+
}
2510+
2511+
/**
2512+
* BITXOR.
2513+
*
2514+
* Returns the bitwise XOR of two integer values.
2515+
*
2516+
* Excel Function:
2517+
* BITXOR(number1, number2)
2518+
*
2519+
* @category Engineering Functions
2520+
*
2521+
* @param int $number1
2522+
* @param int $number2
2523+
*
2524+
* @return int|string
2525+
*/
2526+
public static function BITXOR($number1, $number2)
2527+
{
2528+
try {
2529+
$number1 = self::validateBitwiseArgument($number1);
2530+
$number2 = self::validateBitwiseArgument($number2);
2531+
} catch (Exception $e) {
2532+
return $e->getMessage();
2533+
}
2534+
2535+
return $number1 ^ $number2;
2536+
}
2537+
2538+
/**
2539+
* BITLSHIFT.
2540+
*
2541+
* Returns the number value shifted left by shift_amount bits.
2542+
*
2543+
* Excel Function:
2544+
* BITLSHIFT(number, shift_amount)
2545+
*
2546+
* @category Engineering Functions
2547+
*
2548+
* @param int $number
2549+
* @param int $shiftAmount
2550+
*
2551+
* @return int|string
2552+
*/
2553+
public static function BITLSHIFT($number, $shiftAmount)
2554+
{
2555+
try {
2556+
$number = self::validateBitwiseArgument($number);
2557+
} catch (Exception $e) {
2558+
return $e->getMessage();
2559+
}
2560+
2561+
$shiftAmount = Functions::flattenSingleValue($shiftAmount);
2562+
2563+
$result = $number << $shiftAmount;
2564+
if ($result > pow(2, 48) - 1) {
2565+
return Functions::NAN();
2566+
}
2567+
2568+
return $result;
2569+
}
2570+
2571+
/**
2572+
* BITRSHIFT.
2573+
*
2574+
* Returns the number value shifted right by shift_amount bits.
2575+
*
2576+
* Excel Function:
2577+
* BITRSHIFT(number, shift_amount)
2578+
*
2579+
* @category Engineering Functions
2580+
*
2581+
* @param int $number
2582+
* @param int $shiftAmount
2583+
*
2584+
* @return int|string
2585+
*/
2586+
public static function BITRSHIFT($number, $shiftAmount)
2587+
{
2588+
try {
2589+
$number = self::validateBitwiseArgument($number);
2590+
} catch (Exception $e) {
2591+
return $e->getMessage();
2592+
}
2593+
2594+
$shiftAmount = Functions::flattenSingleValue($shiftAmount);
2595+
2596+
return $number >> $shiftAmount;
2597+
}
2598+
24262599
/**
24272600
* ERF.
24282601
*

src/PhpSpreadsheet/Calculation/functionlist.txt

+5
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ BIN2DEC
3232
BIN2HEX
3333
BIN2OCT
3434
BINOMDIST
35+
BITAND
36+
BITLSHIFT
37+
BITOR
38+
BITRSHIFT
39+
BITXOR
3540
CEILING
3641
CELL
3742
CHAR

tests/PhpSpreadsheetTests/Calculation/EngineeringTest.php

+85
Original file line numberDiff line numberDiff line change
@@ -623,6 +623,91 @@ public function providerOCT2HEX()
623623
return require 'data/Calculation/Engineering/OCT2HEX.php';
624624
}
625625

626+
/**
627+
* @dataProvider providerBITAND
628+
*
629+
* @param mixed $expectedResult
630+
* @param mixed[] $args
631+
*/
632+
public function testBITAND($expectedResult, array $args)
633+
{
634+
$result = Engineering::BITAND(...$args);
635+
self::assertEquals($expectedResult, $result, null);
636+
}
637+
638+
public function providerBITAND()
639+
{
640+
return require 'data/Calculation/Engineering/BITAND.php';
641+
}
642+
643+
/**
644+
* @dataProvider providerBITOR
645+
*
646+
* @param mixed $expectedResult
647+
* @param mixed[] $args
648+
*/
649+
public function testBITOR($expectedResult, array $args)
650+
{
651+
$result = Engineering::BITOR(...$args);
652+
self::assertEquals($expectedResult, $result, null);
653+
}
654+
655+
public function providerBITOR()
656+
{
657+
return require 'data/Calculation/Engineering/BITOR.php';
658+
}
659+
660+
/**
661+
* @dataProvider providerBITXOR
662+
*
663+
* @param mixed $expectedResult
664+
* @param mixed[] $args
665+
*/
666+
public function testBITXOR($expectedResult, array $args)
667+
{
668+
$result = Engineering::BITXOR(...$args);
669+
self::assertEquals($expectedResult, $result, null);
670+
}
671+
672+
public function providerBITXOR()
673+
{
674+
return require 'data/Calculation/Engineering/BITXOR.php';
675+
}
676+
677+
/**
678+
* @dataProvider providerBITLSHIFT
679+
*
680+
* @param mixed $expectedResult
681+
* @param mixed[] $args
682+
*/
683+
public function testBITLSHIFT($expectedResult, array $args)
684+
{
685+
$result = Engineering::BITLSHIFT(...$args);
686+
self::assertEquals($expectedResult, $result, null);
687+
}
688+
689+
public function providerBITLSHIFT()
690+
{
691+
return require 'data/Calculation/Engineering/BITLSHIFT.php';
692+
}
693+
694+
/**
695+
* @dataProvider providerBITRSHIFT
696+
*
697+
* @param mixed $expectedResult
698+
* @param mixed[] $args
699+
*/
700+
public function testBITRSHIFT($expectedResult, array $args)
701+
{
702+
$result = Engineering::BITRSHIFT(...$args);
703+
self::assertEquals($expectedResult, $result, null);
704+
}
705+
706+
public function providerBITRSHIFT()
707+
{
708+
return require 'data/Calculation/Engineering/BITRSHIFT.php';
709+
}
710+
626711
/**
627712
* @dataProvider providerDELTA
628713
*
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
return [
4+
[
5+
0b101,
6+
[0b10101, 0b100111],
7+
],
8+
[
9+
0b10001000,
10+
[0b11001000, 0b10111000],
11+
],
12+
[
13+
0b00001000,
14+
[0b01001000, 0b10111000],
15+
],
16+
[
17+
'#VALUE!',
18+
['ABC', 'DEF'],
19+
],
20+
];
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
return [
4+
[
5+
0b1100000,
6+
[0b11, 5],
7+
],
8+
[
9+
0b10100,
10+
[0b101, 2],
11+
],
12+
[
13+
'#VALUE!',
14+
['ABC', 5],
15+
],
16+
[
17+
'#NUM!',
18+
[0b01, 48],
19+
],
20+
];

0 commit comments

Comments
 (0)