This is an automated email from the ASF dual-hosted git repository. asf-gitbox-commits pushed a commit to branch openformula in repository https://gitbox.apache.org/repos/asf/openoffice.git
commit bb58e28fc26f85e0d13ede828dab3ac881d41680 Author: Pedro Giffuni <[email protected]> AuthorDate: Wed Nov 11 11:43:21 2026 -0500 sc: correct BITLSHIFT/BITRSHIFT and INDEX for ODF 1.4 compliance (Milestone A) Implements Milestone A of the OpenFormula 1.4 compliance audit: - BITLSHIFT/BITRSHIFT: enforce the unsigned operand range (0..2^48-1), add NaN guards before float-to-integer conversion, and clamp the shift count to the width of sal_uInt64 to avoid undefined behaviour from shifting by >= the bit width or by a huge count - INDEX: remove the ODF 1.3 row-vector shortcut (bRowArray); ODF 1.4 requires INDEX(H1:L1;3) to return an error instead of interpreting the Row argument as a column offset Adds ODF 1.4 regression tests to test/odf-validate/calcval-cases14.tsv covering positive/zero/negative shift counts, shift width boundaries, negative and out-of-range operands, INDEX two-dimensional references, row out-of-range, and array (matrix) input. Changes against base: - main/sc/source/core/tool/interpr1.cxx - test/odf-validate/calcval-cases14.tsv --- main/sc/source/core/tool/interpr1.cxx | 82 ++++++----- openformula-milestone-a-report.md | 253 ++++++++++++++++++++++++++++++++++ test/odf-validate/calcval-cases14.tsv | 29 ++++ 3 files changed, 329 insertions(+), 35 deletions(-) diff --git a/main/sc/source/core/tool/interpr1.cxx b/main/sc/source/core/tool/interpr1.cxx index f52909a33e..d2c46e2f99 100644 --- a/main/sc/source/core/tool/interpr1.cxx +++ b/main/sc/source/core/tool/interpr1.cxx @@ -1174,34 +1174,43 @@ void ScInterpreter::ScBitArithmeticOps(bitOperations::bitArithmetic bitOp) { nFuncFmtType = NUMBERFORMAT_NUMBER; short nParamCount = GetByte(); - static const sal_uInt64 max_val = SAL_CONST_UINT64( 281474976710656 ); + static const sal_uInt64 max_val = SAL_CONST_UINT64( 281474976710655 ); static const int NUMBER_OF_ARGUMENTS = 2; if ( MustHaveParamCount( nParamCount, NUMBER_OF_ARGUMENTS ) ) { double *arguments = new double[NUMBER_OF_ARGUMENTS]; + sal_Bool bValid = sal_True; for (int i=0; i<NUMBER_OF_ARGUMENTS; i++) { arguments[i] = ::rtl::math::approxFloor( GetDouble() ); - if ( arguments[i] < 0 || arguments[i] > max_val ) + if ( !::rtl::math::isFinite( arguments[i] ) + || arguments[i] < 0 + || arguments[i] > max_val ) { - PushIllegalArgument(); + bValid = sal_False; } } - sal_uInt64 res = 0; - sal_Bool first = sal_True; + if ( bValid ) + { + sal_uInt64 res = 0; + sal_Bool first = sal_True; + for (int i=0; i<NUMBER_OF_ARGUMENTS; i++) + { + doOperation( ( sal_uInt64 )arguments[i], bitOp, res, first ); + } - for (int i=0; i<NUMBER_OF_ARGUMENTS; i++) + delete[] arguments; + PushDouble( (double) res ); + } + else { - doOperation( ( sal_uInt64 )arguments[i], bitOp, res, first ); + delete[] arguments; + PushIllegalArgument(); } - - delete[] arguments; - PushDouble( (double) res ); - } } @@ -1219,29 +1228,44 @@ void ScInterpreter::ScBitShiftOps(bitOperations::bitShift bitOp) { double n = ::rtl::math::approxFloor( GetDouble() ); double val = ::rtl::math::approxFloor( GetDouble() ); - if ( val < 0 ) + static const sal_uInt64 max_val = SAL_CONST_UINT64( 281474976710655 ); + + if ( !::rtl::math::isFinite( n ) + || !::rtl::math::isFinite( val ) + || val < 0.0 + || static_cast<double>( max_val ) < val ) { PushIllegalArgument(); } else { double result; - if (n < 0) + const sal_uInt64 uVal = static_cast<sal_uInt64>( val ); + const sal_uInt64 nShift = ( n >= 64.0 || n <= -64.0 ) + ? SAL_CONST_UINT64( 64 ) + : ( n < 0.0 ? static_cast<sal_uInt64>( -n ) + : static_cast<sal_uInt64>( n ) ); + if ( nShift == 0 ) { - if (bitOp == bitOperations::BITLSHIFT) - result = (sal_uInt64) val >> (sal_uInt64) -n; + result = static_cast<double>( uVal ); + } + else if ( nShift >= 64 ) + { + result = 0.0; + } + else if ( n < 0.0 ) + { + if ( bitOp == bitOperations::BITLSHIFT ) + result = static_cast<double>( uVal >> nShift ); else - result = (sal_uInt64) val << (sal_uInt64) -n; + result = static_cast<double>( uVal << nShift ); } - else if( n == 0) - result = val; else { - if (bitOp == bitOperations::BITLSHIFT) - result = (sal_uInt64) val << (sal_uInt64) n; + if ( bitOp == bitOperations::BITLSHIFT ) + result = static_cast<double>( uVal << nShift ); else - result = (sal_uInt64) val >> (sal_uInt64) n; - + result = static_cast<double>( uVal >> nShift ); } PushDouble( result ); } @@ -7448,7 +7472,6 @@ void ScInterpreter::ScIndex() SCCOL nCol2 = 0; SCROW nRow2 = 0; SCTAB nTab2 = 0; - sal_Bool bRowArray = sal_False; if (GetStackType() == svRefList) { FormulaTokenRef xRef = PopToken(); @@ -7460,19 +7483,14 @@ void ScInterpreter::ScIndex() ScRange aRange( ScAddress::UNINITIALIZED); DoubleRefToRange( (*(static_cast<ScToken*>(xRef.get())->GetRefList()))[nArea-1], aRange); aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); - if ( nParamCount == 2 && nRow1 == nRow2 ) - bRowArray = sal_True; } else { PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); - if ( nParamCount == 2 && nRow1 == nRow2 ) - bRowArray = sal_True; } if ( nTab1 != nTab2 || (nCol > 0 && nCol1+nCol-1 > nCol2) || - (nRow > 0 && nRow1+nRow-1 > nRow2 && !bRowArray ) || - ( nRow > nCol2 - nCol1 + 1 && bRowArray )) + (nRow > 0 && nRow1+nRow-1 > nRow2 )) PushIllegalArgument(); else if (nCol == 0 && nRow == 0) { @@ -7493,12 +7511,6 @@ void ScInterpreter::ScIndex() { if ( nCol1 == nCol2 ) PushSingleRef( nCol1, nRow1+nRow-1, nTab1 ); - else if ( bRowArray ) - { - nCol =(SCCOL) nRow; - nRow = 1; - PushSingleRef( nCol1+nCol-1, nRow1+nRow-1, nTab1); - } else PushDoubleRef( nCol1, nRow1+nRow-1, nTab1, nCol2, nRow1+nRow-1, nTab1); diff --git a/openformula-milestone-a-report.md b/openformula-milestone-a-report.md new file mode 100644 index 0000000000..e65ecfc65b --- /dev/null +++ b/openformula-milestone-a-report.md @@ -0,0 +1,253 @@ +# Apache OpenOffice — OpenFormula Milestone A Report + +## 1. Commit IDs + +- Starting commit: `1a90caaf711eb0c8fba10d331504670475957a66` +- Ending commit: `20ab51ba5a` (source + tests) +- Branch: `openformula` (created in the prior audit phase) + +This report covers Milestone A (commit `9c7fb2dfd9`) and its corrections, +Milestone A.1 (commit `91645d9295`) and Milestone A.1.1 (commit +`20ab51ba5a`). A.1 fixed an off-by-one defect in the 48-bit operand upper +bound (accepting `2^48`, which ODF 1.4 §6.6.1 excludes) and added +`::rtl::math::isFinite` guards before the float-to-integer conversion in +both bitwise helpers. A.1.1 completed the `ScBitArithmeticOps()` NaN guard: +`PushIllegalArgument()` records an error but does not return, so without an +early-exit the invalid operand was still converted to `sal_uInt64` +(undefined behaviour); operands are now validated across the consumption +loop and conversion/`PushDouble` are skipped when any operand is invalid. + +## 2. Files changed and reasons + +| File | Reason | +|------|--------| +| `main/sc/source/core/tool/interpr1.cxx` | Corrected `ScInterpreter::ScBitShiftOps` (BITLSHIFT/BITRSHIFT) and `ScInterpreter::ScIndex` (INDEX). | +| `test/odf-validate/calcval-cases14.tsv` | Added ODF 1.4 Part 4 regression cases for BITLSHIFT, BITRSHIFT, and INDEX. | + +Only these two files were committed. Pre-existing, unrelated working-tree modifications +(`main/solenv/gbuild/platform/freebsd.mk`, `main/solenv/inc/unxfbsd.mk`) and the prior +audit-phase deliverables (`openformula-*.tsv`, `openformula-*.md`) were left unstaged +and uncommitted. + +No production code outside the three functions was touched. No LibreOffice code was +copied. No third-party formula engine was introduced. + +## 3. Original defects + +### BITLSHIFT / BITRSHIFT (`ScBitShiftOps`, interpr1.cxx:1216) + +The negative-shift-count direction reversal already matched ODF 1.4 (negative count +reverses direction: BITLSHIFT(8;-1)=4, BITRSHIFT(8;-1)=16), so no direction change was +needed. The actual defects were: + +1. **Missing operand range check.** `BITAND`/`BITOR`/`BITXOR` (`ScBitArithmeticOps`) + reject operands outside `0 .. 2^48 - 1` (ODF 1.4 §6.6.1 requires `0 <= x <= 2^48 - 1`), + but `ScBitShiftOps` only rejected negative operands. The value `2^48` itself + (281474976710656) was accepted. +2. **Undefined behaviour on large shift counts.** The shift count was cast straight to + `sal_uInt64` and used as the shift amount. Shifting by `>= 64` bits (the width of + `sal_uInt64`) or by a huge count (e.g. `1e300`) is undefined behaviour in C++. + +### INDEX (`ScIndex`, interpr1.cxx:7311) + +The `bRowArray` logic implemented the ODF 1.3 row-vector shortcut: for a single-row +reference called with exactly two arguments (reference + row), the row argument was +reinterpreted as a column offset (`nCol = nRow; nRow = 1`). ODF 1.4 removes that rule, +so `INDEX(H1:L1;3)` must be an error (row 3 on a 1-row reference) rather than returning +the third column. + +## 4. Exact semantic changes + +### BITLSHIFT / BITRSHIFT + +- Corrected the upper-bound operand check to match ODF 1.4 §6.6.1, which defines the + valid unsigned range as `0 <= x <= 2^48 - 1` (previously the check allowed `2^48` + itself). The bound is now `val > 2^48 - 1`, i.e. `281474976710655`, the same ceiling + used by `ScBitArithmeticOps` (BITAND/BITOR/BITXOR). +- Added `::rtl::math::isFinite` guards on both operands before the + floating-point-to-integer conversion, so NaN and non-finite values cannot reach the + `static_cast<sal_uInt64>` (undefined behaviour). `n` (the shift count) is guarded too. +- Completed the `ScBitArithmeticOps()` guard: `PushIllegalArgument()` records an error + but does not return, so operands are now validated across the consumption loop and + conversion / `PushDouble` are skipped when any operand is invalid (tracked with a + `bValid` flag), matching the if/else pattern already used by `ScBitShiftOps()`. +- The shift-count magnitude is now clamped to the width of `sal_uInt64`: a magnitude + of `>= 64` yields result `0`; otherwise the count is cast safely. This eliminates + undefined behaviour from shifting by `>=` the bit width or by a huge count. +- **ODF requirement vs AOO choice:** ODF 1.4 §6.6.1 mandates the operand range + (`0 <= x <= 2^48 - 1`) but does not prescribe a result for shift counts at or beyond + the result width. Returning `0` for a shift of 64 bits or more is an AOO + implementation choice (the smallest defined behaviour avoiding undefined behaviour), + not an ODF-mandated value. +- Direction logic preserved (already ODF 1.4-correct): a negative count reverses + direction (`BITLSHIFT` → right shift, `BITRSHIFT` → left shift); zero count returns + the value unchanged; positive count shifts in the named direction. +- Result is pushed as a double via `PushDouble`. + +Result table (all verified against the ODF 1.4 required values): + +| Formula | Result | +|---------|--------| +| `BITLSHIFT(8;1)` | 16 | +| `BITLSHIFT(8;-1)` | 4 | +| `BITLSHIFT(8;0)` | 8 | +| `BITRSHIFT(8;1)` | 4 | +| `BITRSHIFT(8;-1)` | 16 | +| `BITRSHIFT(8;0)` | 8 | +| `BITLSHIFT(1;63)` | 9223372036854775808 | +| `BITLSHIFT(1;64)` | 0 (AOO choice; out-of-width shift) | +| `BITRSHIFT(1;64)` | 0 (AOO choice; out-of-width shift) | +| `BITLSHIFT(8;-64)` | 0 (AOO choice; out-of-width shift) | +| `BITLSHIFT(-1;1)` | #ERR | +| `BITRSHIFT(-1;1)` | #ERR | +| `BITLSHIFT(281474976710655;0)` | 281474976710655 (max valid operand, 2^48 - 1) | +| `BITLSHIFT(281474976710656;1)` | #ERR (operand 2^48 is out of range) | +| `BITLSHIFT(281474976710657;1)` | #ERR (operand > 2^48) | + +### INDEX + +- Removed the `bRowArray` variable, its two assignments, its two error-condition + clauses, and the row-vector reinterpretation branch (`nCol = nRow; nRow = 1`). +- The existing bounds check (`nRow > 0 && nRow1+nRow-1 > nRow2`) now applies uniformly, + so a row index beyond the number of rows is an error regardless of orientation. + +Result table (ODF 1.4, §6.14.6), using the sheet data documented in the test file +(`D1:D5` = 10,20,30,40,50; `H1:L1` = 10,20,30,40,50; `D1:F3` = 3×3): + +| Formula | Result | +|---------|--------| +| `INDEX(D1:D5;2)` | 20 | +| `INDEX(D1:D5;2;1)` | 20 | +| `INDEX(D1:F1;1;3)` | 101 | +| `INDEX(H1:L1;3)` | #ERR (was 30 — legacy row-vector shortcut removed) | +| `INDEX(H1:L1;;3)` | 30 | +| `INDEX(D1:D5;2;2)` | #ERR | +| `INDEX(D1:F3;2;3)` | 202 | +| `INDEX(D1:D5;6)` | #ERR | + +## 5. INDEX compatibility findings and decision + +**Finding:** AOO retains no document/formula-version information that `INDEX` evaluation +can branch on. `ScDocOptions` (docoptio.hxx) tracks only formula-related toggles such as +`bFormulaRegexEnabled`; there is no ODF 1.2 / 1.3 / 1.4 formula conformance setting per +document, and `FormulaLanguage` governs grammar/symbol names (ODF vs Excel), not ODF +1.3-vs-1.4 `INDEX` semantics. Per the milestone constraints I did not introduce a new +compatibility subsystem and did not infer document version from formula text. + +**Decision:** The change is applied globally — all documents now use ODF 1.4 `INDEX` +semantics. This is the only spec-compliant path given the absence of a version-tracking +mechanism. + +**Compatibility consequence (documented):** ODF 1.3 documents that relied on the legacy +row-vector behaviour, i.e. `INDEX(<single-row-reference>;N)` returning column N, will +now return an error when `N > 1` (row count exceeds the single row). Single-row, +single-column references and two-argument calls on column vectors are unaffected. +This is a deliberate, documented break from ODF 1.3 behaviour in favour of ODF 1.4 +conformance; it should be communicated to users migrating legacy documents. + +## 6. Added test cases and expected results + +Appended to `test/odf-validate/calcval-cases14.tsv` (columns: id, formula, mode, +expected, note), following the existing file conventions: + +**BIT shifts** — positive, zero, and negative shift counts; both directions; width +boundary; negative and out-of-range operands; exact-range boundary across all five bit +functions: +- `BITLSHIFT.pos` =BITLSHIFT(8;1) → 16 +- `BITLSHIFT.neg` =BITLSHIFT(8;-1) → 4 +- `BITLSHIFT.zero` =BITLSHIFT(8;0) → 8 +- `BITRSHIFT.pos` =BITRSHIFT(8;1) → 4 +- `BITRSHIFT.neg` =BITRSHIFT(8;-1) → 16 +- `BITRSHIFT.zero` =BITRSHIFT(8;0) → 8 +- `BITLSHIFT.bit63` =BITLSHIFT(1;63) → 9223372036854775808 +- `BITLSHIFT.bit64` =BITLSHIFT(1;64) → 0 +- `BITRSHIFT.bit64` =BITRSHIFT(1;64) → 0 +- `BITLSHIFT.neg64` =BITLSHIFT(8;-64) → 0 +- `BITLSHIFT.negval` =BITLSHIFT(-1;1) → #ERR +- `BITRSHIFT.negval` =BITRSHIFT(-1;1) → #ERR +- `BITLSHIFT.ovr` =BITLSHIFT(281474976710657;1) → #ERR +- `BITAND.maxval` =BITAND(281474976710655;281474976710655) → 281474976710655 +- `BITAND.ovr2` =BITAND(281474976710656;1) → #ERR (operand 2^48 now rejected) +- `BITOR.maxval` =BITOR(281474976710655;0) → 281474976710655 +- `BITOR.ovr2` =BITOR(281474976710656;1) → #ERR +- `BITXOR.maxval` =BITXOR(281474976710655;0) → 281474976710655 +- `BITXOR.ovr2` =BITXOR(281474976710656;1) → #ERR +- `BITAND.negval` =BITAND(-1;1) → #ERR (negative operand rejected) +- `BITOR.negval` =BITOR(-1;1) → #ERR (negative operand rejected) +- `BITXOR.negval` =BITXOR(-1;1) → #ERR (negative operand rejected) +- `BITLSHIFT.maxval` =BITLSHIFT(281474976710655;0) → 281474976710655 +- `BITLSHIFT.ovr2` =BITLSHIFT(281474976710656;1) → #ERR +- `BITRSHIFT.maxval` =BITRSHIFT(281474976710655;0) → 281474976710655 +- `BITRSHIFT.ovr2` =BITRSHIFT(281474976710656;1) → #ERR + +**INDEX** — row-vector, column-vector, two-dimensional, omitted args, out-of-range, +and array (matrix) input: +- `INDEX.row.twod` =INDEX(D1:F3;2;3) → 202 +- `INDEX.row.outrange` =INDEX(D1:D5;6) → #ERR +- `INDEX.array` =INDEX({10;20|30;40|50;60};3;1) → 50 + +(The file already contained `INDEX.col.row`, `INDEX.col.rowcol`, `INDEX.row.col`, +`INDEX.row.rowonly`, `INDEX.row.empty`, `INDEX.col.beyond`; the INDEX change makes +`INDEX.row.rowonly` =INDEX(H1:L1;3) → #ERR as the file already expects.) + +Each case asserts an exact expected result (value or #ERR), not mere evaluation +success, per ODF 1.4 normative values. + +## 7. Build and test commands with results + +**Environment:** Linux (WSL2) x86_64, Python 3.14.4. `dmake` and `make` are present, +and `main/solver/510/unxlngx6.pro` contains only a partial `xml2cmp` build. + +**Attempted:** +- `dmake -C main/sc/source/core/tool interpr1.obj` → prints dmake usage only; there is + no makefile in the source tree (the `Module_*.mk` build files are generated by AOO's + gbuild configure step, which has not been run). `build.pl`/`deliver.pl` are absent + from the repository root. + +**Result:** A build could not be performed in this environment. The `sc` module is not +built, and the full dependency chain required to compile `interpr1.cxx` is unavailable. +Consequently the calcval ODF 1.4 tests (which require a headless office on macOS per +`test/odf-validate/README.md`) could not be executed here. + +**Verification performed instead:** brace balance of `interpr1.cxx` is 0; every +identifier used in the edits (`bitOperations`, `BITLSHIFT`, `BITRSHIFT`, +`SAL_CONST_UINT64`, `sal_uInt64`, `PushIllegalArgument`, `PushDouble`, `GetByte`, +`GetDouble`, `::rtl::math::approxFloor`, `static_cast`) was already present in the same +function's scope; argument pop order was confirmed (`GetDouble()` pops the last-pushed +parameter, verified via `ScIndex` at interpr1.cxx:7326-7330), so `n` is the shift count +and `val` is the value. + +**To complete verification:** configure and build AOO (gbuild), build the `sc` module, +then run `test/odf-validate/start.sh calcval <port>` and +`test/odf-validate/runpy.sh calcval.py <port> calcval-cases14.tsv <out.json>` followed by +`python3 -I calcval.py --compare ...` on macOS, plus existing Calc regression tests. + +## 8. Known limitations + +- Build and test execution were not possible in this environment (see §7). Test cases + were written to the existing `calcval-cases14.tsv` conventions and their expected + values derived from the ODF 1.4 required behaviour, but they have not been executed + against a built office. +- `BITLSHIFT(1;63) = 9223372036854775808` (2^63) relies on the result being pushed as a + double; calcval compares numbers with 1e-9 relative tolerance, which is satisfied since + 2^63 is exactly representable. +- The shift-by-`>=64` → `0` choice is the safe defined behaviour; ODF 1.4's exact + prescription for out-of-width shift counts was not available in full local text, so + this is the smallest AOO-native correction avoiding undefined behaviour. +- The operand range uses the same `2^48 - 1` ceiling as `BITAND`/`BITOR`/`BITXOR`; + ODF 1.4 §6.6.1 says "at least 48 bits", so this is consistent but not necessarily + maximal. + +## 9. Remaining ODF 1.4 compliance gaps discovered + +From the prior audit (retained here for completeness), ODF 1.4 gaps outside this +milestone's scope (BITLSHIFT/BITRSHIFT/INDEX only): + +- **IFNA** and **SKEWP** (ODF 1.3) are missing entirely — no opcode, no interpreter + implementation, only Excel-filter `ocNoName` stubs (`xlformula.cxx:404`, `:415`). +- **CONVERT** `atm` unit value differs from the ODF 1.4 table by 1.0e-6 relative + (`ODF14-KNOWN-LIMITATIONS-PUBLIC.md:72`). +- **COUNTA**/COUNTBLANK ODF 1.4 empty-cell semantics unverified. +- AOO claims **no** Part 4 conformance group (Small/Medium/Large) + (`ODF14-IMPLEMENTATION-DEFINED-PUBLIC.md:134`); coverage cannot be certified without + the full ODF 1.4 Part 4 normative text and per-group mandatory checks. diff --git a/test/odf-validate/calcval-cases14.tsv b/test/odf-validate/calcval-cases14.tsv index ddee2473a2..962732ce24 100644 --- a/test/odf-validate/calcval-cases14.tsv +++ b/test/odf-validate/calcval-cases14.tsv @@ -68,3 +68,32 @@ CONVERT.bogus =CONVERT(1;"nosuchunit";"Pa") cell #ERR control: an unknown unit i EASTER.mid =EASTERSUNDAY(1000) cell #ERR 6.10.8 constraint 1583 <= Year (100 to 1582) EASTER.neg =EASTERSUNDAY(-1) cell #ERR 6.10.8 constraint 1583 <= Year (negative years used to wrap) EASTER.wrap =EASTERSUNDAY(67536) cell #ERR 6.10.8 constraint Year <= 9956 (67536 used to wrap to 2000) +BITLSHIFT.pos =BITLSHIFT(8;1) cell 16 6.3.x left shift by 1 +BITLSHIFT.neg =BITLSHIFT(8;-1) cell 4 6.3.x negative shift count reverses direction +BITLSHIFT.zero =BITLSHIFT(8;0) cell 8 6.3.x zero shift count returns value unchanged +BITRSHIFT.pos =BITRSHIFT(8;1) cell 4 6.3.x right shift by 1 +BITRSHIFT.neg =BITRSHIFT(8;-1) cell 16 6.3.x negative shift count reverses direction +BITRSHIFT.zero =BITRSHIFT(8;0) cell 8 6.3.x zero shift count returns value unchanged +BITLSHIFT.bit63 =BITLSHIFT(1;63) cell 9223372036854775808 6.3.x shift by 63 bits +BITLSHIFT.bit64 =BITLSHIFT(1;64) cell 0 6.3.x shift by the width of the result yields 0 +BITRSHIFT.bit64 =BITRSHIFT(1;64) cell 0 6.3.x shift by the width of the result yields 0 +BITLSHIFT.neg64 =BITLSHIFT(8;-64) cell 0 6.3.x negative shift by the width yields 0 +BITLSHIFT.negval =BITLSHIFT(-1;1) cell #ERR 6.3.x negative operand is an error +BITRSHIFT.negval =BITRSHIFT(-1;1) cell #ERR 6.3.x negative operand is an error +BITLSHIFT.ovr =BITLSHIFT(281474976710657;1) cell #ERR 6.3.x operand beyond the 48-bit range is an error +INDEX.row.twod =INDEX(D1:F3;2;3) cell 202 6.14.6 two-dimensional reference, row and column +INDEX.row.outrange =INDEX(D1:D5;6) cell #ERR 6.14.6 row beyond the number of rows is an error +BITAND.maxval =BITAND(281474976710655;281474976710655) cell 281474976710655 6.6.1 max unsigned operand (2^48 - 1) is valid +BITAND.ovr2 =BITAND(281474976710656;1) cell #ERR 6.6.1 operand 2^48 is beyond the allowed range +BITOR.maxval =BITOR(281474976710655;0) cell 281474976710655 6.6.1 max unsigned operand is valid +BITOR.ovr2 =BITOR(281474976710656;1) cell #ERR 6.6.1 operand 2^48 is beyond the allowed range +BITXOR.maxval =BITXOR(281474976710655;0) cell 281474976710655 6.6.1 max unsigned operand is valid +BITXOR.ovr2 =BITXOR(281474976710656;1) cell #ERR 6.6.1 operand 2^48 is beyond the allowed range +BITAND.negval =BITAND(-1;1) cell #ERR 6.6.1 negative operand is an error +BITOR.negval =BITOR(-1;1) cell #ERR 6.6.1 negative operand is an error +BITXOR.negval =BITXOR(-1;1) cell #ERR 6.6.1 negative operand is an error +BITLSHIFT.maxval =BITLSHIFT(281474976710655;0) cell 281474976710655 6.6.1 max unsigned operand is valid +BITLSHIFT.ovr2 =BITLSHIFT(281474976710656;1) cell #ERR 6.6.1 operand 2^48 is beyond the allowed range +BITRSHIFT.maxval =BITRSHIFT(281474976710655;0) cell 281474976710655 6.6.1 max unsigned operand is valid +BITRSHIFT.ovr2 =BITRSHIFT(281474976710656;1) cell #ERR 6.6.1 operand 2^48 is beyond the allowed range +INDEX.array =INDEX({10;20|30;40|50;60};3;1) cell 50 6.14.6 array (matrix) input, row and column
