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 exp from 'constants';
|
||||
|
||||
describe("calc", () => {
|
||||
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);
|
||||
});
|
||||
|
||||
describe('calc.flipBit', () => {
|
||||
it('calculates flipped bit 32-bit number', () => {
|
||||
expect(calc.flipBit(0, 31)).toBe(1);
|
||||
expect(calc.flipBit(1, 31)).toBe(0);
|
||||
@@ -54,7 +26,26 @@ describe("calc", () => {
|
||||
expect(calc.flipBit(-1, 0)).toBe(2147483647);
|
||||
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', () => {
|
||||
expect(calc.applyTwosComplement("010")).toBe("110");
|
||||
expect(calc.applyTwosComplement("110")).toBe("010"); // reverse
|
||||
@@ -63,6 +54,48 @@ describe("calc", () => {
|
||||
expect(calc.applyTwosComplement("10101100")).toBe("01010100");
|
||||
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', () => {
|
||||
expect(calc.flipBit(calc.promoteToBigInt(-1), 31).toString()).toBe("8589934591");
|
||||
@@ -71,10 +104,13 @@ describe("calc", () => {
|
||||
it('promotes to BigInt with the same bits', () => {
|
||||
expect(calc.promoteToBigInt(-1).toString(2)).toBe("11111111111111111111111111111111");
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
describe("bitwise ", () => {
|
||||
|
||||
|
||||
|
||||
it("NOT same as in node", () => {
|
||||
|
||||
|
||||
@@ -1,12 +1,19 @@
|
||||
import { type } from "os";
|
||||
import { Expression } from "../expression/expression-interfaces";
|
||||
import formatter from "./formatter";
|
||||
import { NumberType } from "./types";
|
||||
import { asIntN } from "./utils";
|
||||
|
||||
export default {
|
||||
abs (num : NumberType) : NumberType {
|
||||
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) {
|
||||
return typeof num == 'bigint' ? 64 : 32
|
||||
@@ -26,31 +33,21 @@ export default {
|
||||
return Math.max.apply(null, counts);
|
||||
},
|
||||
|
||||
calcExpression: function (expr: Expression) {
|
||||
return eval(expr.expressionString);
|
||||
},
|
||||
|
||||
flipBit: function(num: number|bigint, index: number): number|bigint {
|
||||
flipBit: function(num: NumberType, index: number): NumberType {
|
||||
|
||||
const is64bit = typeof num == 'bigint';
|
||||
const size = typeof num == "bigint" ? 64 : 32;
|
||||
const bin = formatter.bin(num).padStart(size, '0');
|
||||
const staysNegative = (bin[0] == "1" && index > 0);
|
||||
const becomesNegative = (bin[0] == "0" && index == 0);
|
||||
|
||||
//console.log(bin);
|
||||
|
||||
let m = 1;
|
||||
let flipped = bin.substring(0, index) + flip(bin[index]) + bin.substring(index+1);
|
||||
|
||||
//console.log(flipped);
|
||||
|
||||
if(staysNegative || becomesNegative) {
|
||||
flipped = this.applyTwosComplement(flipped);
|
||||
m=-1;
|
||||
}
|
||||
|
||||
//console.log(flipped);
|
||||
|
||||
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("");
|
||||
},
|
||||
|
||||
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 => {
|
||||
|
||||
var padded = bin
|
||||
|
||||
@@ -13,16 +13,16 @@ describe("formatter", () => {
|
||||
});
|
||||
|
||||
it('formats large binary number correctly', () => {
|
||||
var decimal = 68719476735;
|
||||
var binary = formatter.bin(68719476735);
|
||||
var decimal = BigInt("68719476735");
|
||||
var binary = formatter.bin(decimal);
|
||||
var hex = formatter.numberToString(decimal, 'hex');
|
||||
expect(binary).toBe('111111111111111111111111111111111111');
|
||||
expect(hex).toBe('0xfffffffff');
|
||||
});
|
||||
|
||||
it('formats negative binary numbers', () => {
|
||||
//expect(formatter.numberToString(-1, 'bin')).toBe("11111111111111111111111111111111");
|
||||
//expect(formatter.numberToString(-0, 'bin')).toBe("0");
|
||||
expect(formatter.numberToString(-1, 'bin')).toBe("11111111111111111111111111111111");
|
||||
expect(formatter.numberToString(-0, 'bin')).toBe("0");
|
||||
expect(formatter.numberToString(-2147483647, 'bin')).toBe("10000000000000000000000000000001");
|
||||
});
|
||||
|
||||
|
||||
@@ -3,21 +3,14 @@ import { NumberType } from "./types";
|
||||
export type NumberBase = 'dec' | 'hex' | 'bin';
|
||||
|
||||
const formatter = {
|
||||
numberToString: function(num: number|bigint, base: NumberBase) : string {
|
||||
numberToString: function(num: NumberType, base: NumberBase) : string {
|
||||
|
||||
switch(base) {
|
||||
case 'hex':
|
||||
var hexVal = calc.abs(num).toString(16);
|
||||
return num >= 0 ? '0x' + hexVal : '-0x' + hexVal;
|
||||
case 'bin':
|
||||
|
||||
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);
|
||||
return calc.binaryRepresentation(num);
|
||||
case 'dec':
|
||||
return num.toString(10);
|
||||
default:
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { NumberType } from "./types";
|
||||
|
||||
function chunkifyString(input: string, chunkSize: number) : string[] {
|
||||
|
||||
const result : string[] = [];
|
||||
@@ -9,4 +11,8 @@ function chunkifyString(input: string, chunkSize: number) : string[] {
|
||||
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 ScalarValue from "./ScalarValue";
|
||||
|
||||
@@ -20,7 +21,7 @@ function evalute(op1 : NumberType, operator: string, op2 : NumberType) : NumberT
|
||||
switch(operator) {
|
||||
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);
|
||||
|
||||
@@ -13,7 +13,7 @@ interface ParserConfig {
|
||||
}
|
||||
|
||||
export interface ParsedNumber {
|
||||
value: number|bigint;
|
||||
value: NumberType;
|
||||
base: NumberBase;
|
||||
input: string;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user