mirror of
https://github.com/BorysLevytskyi/BitwiseCmd.git
synced 2025-12-10 06:52:05 +01:00
Correct support rshift for 64-bit numbers
This commit is contained in:
@@ -3,35 +3,7 @@ import { BitwiseOperationExpression, ScalarValue, BitwiseOperator } from '../exp
|
|||||||
import { INT32_MAX_VALUE } from './const';
|
import { INT32_MAX_VALUE } from './const';
|
||||||
import exp from 'constants';
|
import exp from 'constants';
|
||||||
|
|
||||||
describe("calc", () => {
|
describe('calc.flipBit', () => {
|
||||||
it('calculates number of bits', () => {
|
|
||||||
expect(calc.numberOfBitsDisplayed(1)).toBe(1);
|
|
||||||
expect(calc.numberOfBitsDisplayed(BigInt(-1))).toBe(64);
|
|
||||||
expect(calc.numberOfBitsDisplayed(2)).toBe(2);
|
|
||||||
expect(calc.numberOfBitsDisplayed(3)).toBe(2);
|
|
||||||
expect(calc.numberOfBitsDisplayed(68719476735)).toBe(36);
|
|
||||||
expect(calc.numberOfBitsDisplayed(-INT32_MAX_VALUE)).toBe(32);
|
|
||||||
expect(calc.numberOfBitsDisplayed(-(BigInt(INT32_MAX_VALUE+1)))).toBe(64);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('calculates max number of bits', () => {
|
|
||||||
expect(calc.maxNumberOfBitsDisplayed([1, 2, 3, 10])).toBe(4);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('calculates expression', () => {
|
|
||||||
|
|
||||||
var result = calc.calcExpression(new BitwiseOperationExpression(
|
|
||||||
"1|2&3",
|
|
||||||
[
|
|
||||||
new ScalarValue(1),
|
|
||||||
new BitwiseOperator(new ScalarValue(2), "|"),
|
|
||||||
new BitwiseOperator(new ScalarValue(3), "&"),
|
|
||||||
]
|
|
||||||
));
|
|
||||||
|
|
||||||
expect(result).toBe(3);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('calculates flipped bit 32-bit number', () => {
|
it('calculates flipped bit 32-bit number', () => {
|
||||||
expect(calc.flipBit(0, 31)).toBe(1);
|
expect(calc.flipBit(0, 31)).toBe(1);
|
||||||
expect(calc.flipBit(1, 31)).toBe(0);
|
expect(calc.flipBit(1, 31)).toBe(0);
|
||||||
@@ -54,7 +26,26 @@ describe("calc", () => {
|
|||||||
expect(calc.flipBit(-1, 0)).toBe(2147483647);
|
expect(calc.flipBit(-1, 0)).toBe(2147483647);
|
||||||
expect(calc.flipBit(2147483647, 30)).toBe(2147483645);
|
expect(calc.flipBit(2147483647, 30)).toBe(2147483645);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('calc.numberOfBitsDisplayed', () => {
|
||||||
|
it('calculates number of bits', () => {
|
||||||
|
expect(calc.numberOfBitsDisplayed(1)).toBe(1);
|
||||||
|
expect(calc.numberOfBitsDisplayed(BigInt(-1))).toBe(64);
|
||||||
|
expect(calc.numberOfBitsDisplayed(2)).toBe(2);
|
||||||
|
expect(calc.numberOfBitsDisplayed(3)).toBe(2);
|
||||||
|
expect(calc.numberOfBitsDisplayed(68719476735)).toBe(36);
|
||||||
|
expect(calc.numberOfBitsDisplayed(-INT32_MAX_VALUE)).toBe(32);
|
||||||
|
expect(calc.numberOfBitsDisplayed(-(BigInt(INT32_MAX_VALUE+1)))).toBe(64);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('maxNumberOfBitsDisplayed', () => {
|
||||||
|
expect(calc.maxNumberOfBitsDisplayed([1, 2, 3, 10])).toBe(4);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('calc.applyTwosComplement', () => {
|
||||||
it('applies twos complement', () => {
|
it('applies twos complement', () => {
|
||||||
expect(calc.applyTwosComplement("010")).toBe("110");
|
expect(calc.applyTwosComplement("010")).toBe("110");
|
||||||
expect(calc.applyTwosComplement("110")).toBe("010"); // reverse
|
expect(calc.applyTwosComplement("110")).toBe("010"); // reverse
|
||||||
@@ -63,6 +54,48 @@ describe("calc", () => {
|
|||||||
expect(calc.applyTwosComplement("10101100")).toBe("01010100");
|
expect(calc.applyTwosComplement("10101100")).toBe("01010100");
|
||||||
expect(calc.applyTwosComplement("01010100")).toBe("10101100"); // reverse
|
expect(calc.applyTwosComplement("01010100")).toBe("10101100"); // reverse
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('calc.rshift', () => {
|
||||||
|
|
||||||
|
it('produces number when given number and vice vers', () => {
|
||||||
|
const number = calc.rshift(1, 1, 32);
|
||||||
|
const bigInt = calc.rshift(BigInt(1), 1, 32);
|
||||||
|
|
||||||
|
expect(typeof number).toBe('number');
|
||||||
|
expect(number).toBe(2);
|
||||||
|
|
||||||
|
expect(typeof bigInt).toBe('bigint');
|
||||||
|
expect(bigInt.toString()).toBe('2');
|
||||||
|
});
|
||||||
|
|
||||||
|
it("respects bit size", () => {
|
||||||
|
expect(calc.rshift(BigInt("0b0100"), 2, 4).toString()).toBe("0");
|
||||||
|
});
|
||||||
|
|
||||||
|
it('transitions number to negative', ()=> {
|
||||||
|
// 4-bit space
|
||||||
|
expect(calc.rshift(BigInt("0b0100"), 1, 4).toString()).toBe("-8");
|
||||||
|
|
||||||
|
// 5-bit space
|
||||||
|
expect(calc.rshift(BigInt("0b00100"), 1, 5).toString()).toBe("8");
|
||||||
|
expect(calc.rshift(BigInt("0b01000"), 1, 5).toString()).toBe("-16");
|
||||||
|
|
||||||
|
// 32-bit
|
||||||
|
expect(calc.rshift(BigInt("2147483647"), 1, 32).toString()).toBe("-2");
|
||||||
|
expect(calc.rshift(BigInt("2147483647"), 2, 32).toString()).toBe("-4");
|
||||||
|
|
||||||
|
// 64-bit
|
||||||
|
expect(calc.rshift(BigInt("9223372036854775807"), 1, 64).toString()).toBe("-2");
|
||||||
|
expect(calc.rshift(BigInt("9223372036854775807"), 2, 64).toString()).toBe("-4");
|
||||||
|
expect(calc.rshift(BigInt("2147483647"), 1, 64).toString()).toBe("4294967294");
|
||||||
|
expect(calc.rshift(BigInt("2147483647"), 2, 64).toString()).toBe("8589934588");
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("calc misc", () => {
|
||||||
|
|
||||||
|
|
||||||
it('calcualte 31th bit in 64-bit int', () => {
|
it('calcualte 31th bit in 64-bit int', () => {
|
||||||
expect(calc.flipBit(calc.promoteToBigInt(-1), 31).toString()).toBe("8589934591");
|
expect(calc.flipBit(calc.promoteToBigInt(-1), 31).toString()).toBe("8589934591");
|
||||||
@@ -71,10 +104,13 @@ describe("calc", () => {
|
|||||||
it('promotes to BigInt with the same bits', () => {
|
it('promotes to BigInt with the same bits', () => {
|
||||||
expect(calc.promoteToBigInt(-1).toString(2)).toBe("11111111111111111111111111111111");
|
expect(calc.promoteToBigInt(-1).toString(2)).toBe("11111111111111111111111111111111");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("bitwise ", () => {
|
describe("bitwise ", () => {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
it("NOT same as in node", () => {
|
it("NOT same as in node", () => {
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,19 @@
|
|||||||
|
import { type } from "os";
|
||||||
import { Expression } from "../expression/expression-interfaces";
|
import { Expression } from "../expression/expression-interfaces";
|
||||||
import formatter from "./formatter";
|
import formatter from "./formatter";
|
||||||
import { NumberType } from "./types";
|
import { NumberType } from "./types";
|
||||||
|
import { asIntN } from "./utils";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
abs (num : NumberType) : NumberType {
|
abs (num : NumberType) : NumberType {
|
||||||
return num >= 0 ? num : -num;
|
return num >= 0 ? num : -num;
|
||||||
},
|
},
|
||||||
numberOfBitsDisplayed: function (num: number|bigint) : number {
|
|
||||||
|
maxBitSize(num : NumberType) : number {
|
||||||
|
return typeof num == "bigint" ? 64 : 32;
|
||||||
|
},
|
||||||
|
|
||||||
|
numberOfBitsDisplayed: function (num: NumberType) : number {
|
||||||
|
|
||||||
if(num < 0) {
|
if(num < 0) {
|
||||||
return typeof num == 'bigint' ? 64 : 32
|
return typeof num == 'bigint' ? 64 : 32
|
||||||
@@ -26,31 +33,21 @@ export default {
|
|||||||
return Math.max.apply(null, counts);
|
return Math.max.apply(null, counts);
|
||||||
},
|
},
|
||||||
|
|
||||||
calcExpression: function (expr: Expression) {
|
flipBit: function(num: NumberType, index: number): NumberType {
|
||||||
return eval(expr.expressionString);
|
|
||||||
},
|
|
||||||
|
|
||||||
flipBit: function(num: number|bigint, index: number): number|bigint {
|
|
||||||
|
|
||||||
const is64bit = typeof num == 'bigint';
|
const is64bit = typeof num == 'bigint';
|
||||||
const size = typeof num == "bigint" ? 64 : 32;
|
const size = typeof num == "bigint" ? 64 : 32;
|
||||||
const bin = formatter.bin(num).padStart(size, '0');
|
const bin = formatter.bin(num).padStart(size, '0');
|
||||||
const staysNegative = (bin[0] == "1" && index > 0);
|
const staysNegative = (bin[0] == "1" && index > 0);
|
||||||
const becomesNegative = (bin[0] == "0" && index == 0);
|
const becomesNegative = (bin[0] == "0" && index == 0);
|
||||||
|
|
||||||
//console.log(bin);
|
|
||||||
|
|
||||||
let m = 1;
|
let m = 1;
|
||||||
let flipped = bin.substring(0, index) + flip(bin[index]) + bin.substring(index+1);
|
let flipped = bin.substring(0, index) + flip(bin[index]) + bin.substring(index+1);
|
||||||
|
|
||||||
//console.log(flipped);
|
|
||||||
|
|
||||||
if(staysNegative || becomesNegative) {
|
if(staysNegative || becomesNegative) {
|
||||||
flipped = this.applyTwosComplement(flipped);
|
flipped = this.applyTwosComplement(flipped);
|
||||||
m=-1;
|
m=-1;
|
||||||
}
|
}
|
||||||
|
|
||||||
//console.log(flipped);
|
|
||||||
|
|
||||||
return is64bit ? BigInt("0b"+ flipped)*BigInt(m) : parseInt(flipped, 2)*m;
|
return is64bit ? BigInt("0b"+ flipped)*BigInt(m) : parseInt(flipped, 2)*m;
|
||||||
},
|
},
|
||||||
@@ -83,7 +80,38 @@ export default {
|
|||||||
return bin.split('').map(b => b=="1"?"0":"1").join("");
|
return bin.split('').map(b => b=="1"?"0":"1").join("");
|
||||||
},
|
},
|
||||||
|
|
||||||
bitwise: {
|
binaryRepresentation(num : NumberType, bitSize?: number) : string {
|
||||||
|
|
||||||
|
bitSize = bitSize || typeof num == "bigint" ? 64 : 32;
|
||||||
|
const bin = this.abs(num).toString(2);
|
||||||
|
|
||||||
|
if(bin.length > bitSize!)
|
||||||
|
throw new Error(`Binary represenation '${bin}' is bigger than the given bit size ${bitSize}`)
|
||||||
|
|
||||||
|
return num < 0
|
||||||
|
? this.applyTwosComplement(bin.padStart(bitSize, '0'))
|
||||||
|
: bin;
|
||||||
|
},
|
||||||
|
|
||||||
|
rshift (num: NumberType, numBytes : NumberType, bitSize: number) : NumberType {
|
||||||
|
|
||||||
|
const bytes = asIntN(numBytes);
|
||||||
|
|
||||||
|
let bin = this.binaryRepresentation(num, bitSize).padStart(bitSize, '0');
|
||||||
|
bin = bin.substring(bytes) + "0".repeat(bytes);
|
||||||
|
|
||||||
|
let m = BigInt(1);
|
||||||
|
|
||||||
|
if(bin['0'] == '1') {
|
||||||
|
bin = this.applyTwosComplement(bin);
|
||||||
|
m = BigInt(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = BigInt("0b" + bin) * m;
|
||||||
|
return typeof num == "bigint" ? result : asIntN(result);
|
||||||
|
},
|
||||||
|
|
||||||
|
bitwise: {
|
||||||
not: (bin: string) : string => {
|
not: (bin: string) : string => {
|
||||||
|
|
||||||
var padded = bin
|
var padded = bin
|
||||||
|
|||||||
@@ -13,16 +13,16 @@ describe("formatter", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('formats large binary number correctly', () => {
|
it('formats large binary number correctly', () => {
|
||||||
var decimal = 68719476735;
|
var decimal = BigInt("68719476735");
|
||||||
var binary = formatter.bin(68719476735);
|
var binary = formatter.bin(decimal);
|
||||||
var hex = formatter.numberToString(decimal, 'hex');
|
var hex = formatter.numberToString(decimal, 'hex');
|
||||||
expect(binary).toBe('111111111111111111111111111111111111');
|
expect(binary).toBe('111111111111111111111111111111111111');
|
||||||
expect(hex).toBe('0xfffffffff');
|
expect(hex).toBe('0xfffffffff');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('formats negative binary numbers', () => {
|
it('formats negative binary numbers', () => {
|
||||||
//expect(formatter.numberToString(-1, 'bin')).toBe("11111111111111111111111111111111");
|
expect(formatter.numberToString(-1, 'bin')).toBe("11111111111111111111111111111111");
|
||||||
//expect(formatter.numberToString(-0, 'bin')).toBe("0");
|
expect(formatter.numberToString(-0, 'bin')).toBe("0");
|
||||||
expect(formatter.numberToString(-2147483647, 'bin')).toBe("10000000000000000000000000000001");
|
expect(formatter.numberToString(-2147483647, 'bin')).toBe("10000000000000000000000000000001");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -3,21 +3,14 @@ import { NumberType } from "./types";
|
|||||||
export type NumberBase = 'dec' | 'hex' | 'bin';
|
export type NumberBase = 'dec' | 'hex' | 'bin';
|
||||||
|
|
||||||
const formatter = {
|
const formatter = {
|
||||||
numberToString: function(num: number|bigint, base: NumberBase) : string {
|
numberToString: function(num: NumberType, base: NumberBase) : string {
|
||||||
|
|
||||||
switch(base) {
|
switch(base) {
|
||||||
case 'hex':
|
case 'hex':
|
||||||
var hexVal = calc.abs(num).toString(16);
|
var hexVal = calc.abs(num).toString(16);
|
||||||
return num >= 0 ? '0x' + hexVal : '-0x' + hexVal;
|
return num >= 0 ? '0x' + hexVal : '-0x' + hexVal;
|
||||||
case 'bin':
|
case 'bin':
|
||||||
|
return calc.binaryRepresentation(num);
|
||||||
if(num < 0) {
|
|
||||||
const size = calc.numberOfBitsDisplayed(num);
|
|
||||||
const absBin = calc.abs(num).toString(2).padStart(size, '0');
|
|
||||||
return calc.applyTwosComplement(absBin);
|
|
||||||
}
|
|
||||||
|
|
||||||
return num.toString(2);
|
|
||||||
case 'dec':
|
case 'dec':
|
||||||
return num.toString(10);
|
return num.toString(10);
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { NumberType } from "./types";
|
||||||
|
|
||||||
function chunkifyString(input: string, chunkSize: number) : string[] {
|
function chunkifyString(input: string, chunkSize: number) : string[] {
|
||||||
|
|
||||||
const result : string[] = [];
|
const result : string[] = [];
|
||||||
@@ -9,4 +11,8 @@ function chunkifyString(input: string, chunkSize: number) : string[] {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
export {chunkifyString};
|
function asIntN(num: NumberType) : number {
|
||||||
|
return typeof num == "bigint" ? parseInt(num.toString()): num as number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export {chunkifyString, asIntN};
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import calc from "../core/calc";
|
||||||
import { NumberType } from "../core/types";
|
import { NumberType } from "../core/types";
|
||||||
import ScalarValue from "./ScalarValue";
|
import ScalarValue from "./ScalarValue";
|
||||||
|
|
||||||
@@ -20,7 +21,7 @@ function evalute(op1 : NumberType, operator: string, op2 : NumberType) : NumberT
|
|||||||
switch(operator) {
|
switch(operator) {
|
||||||
case ">>": return (a >> b) as (NumberType);
|
case ">>": return (a >> b) as (NumberType);
|
||||||
case ">>>": return (a >>> b) as (NumberType);
|
case ">>>": return (a >>> b) as (NumberType);
|
||||||
case "<<": return (a << b) as (NumberType);
|
case "<<": return calc.rshift(a, b, calc.maxBitSize(a));
|
||||||
case "&": return (b & a) as (NumberType);
|
case "&": return (b & a) as (NumberType);
|
||||||
case "|": return (b | a) as (NumberType);
|
case "|": return (b | a) as (NumberType);
|
||||||
case "^": return (b ^ a) as (NumberType);
|
case "^": return (b ^ a) as (NumberType);
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ interface ParserConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ParsedNumber {
|
export interface ParsedNumber {
|
||||||
value: number|bigint;
|
value: NumberType;
|
||||||
base: NumberBase;
|
base: NumberBase;
|
||||||
input: string;
|
input: string;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user