mirror of
https://github.com/BorysLevytskyi/BitwiseCmd.git
synced 2025-12-23 05:02:48 +01:00
Fix bus when converting unsigned to signed numbers
This commit is contained in:
@@ -1,5 +1,15 @@
|
|||||||
import { Integer } from "./Integer"
|
import { Integer } from "./Integer"
|
||||||
import { UINT32_MAX_VALUE } from "./const";
|
import { INT32_MAX_VALUE, UINT32_MAX_VALUE } from "./const";
|
||||||
|
|
||||||
|
it('toString for unsigned', () => {
|
||||||
|
const s = Integer.unsigned(4294967295).toString(2)
|
||||||
|
expect(s).toBe("11111111111111111111111111111111");
|
||||||
|
});
|
||||||
|
|
||||||
|
it('detects size correctly for signed and unsiged version', () => {
|
||||||
|
expect(Integer.unsigned(UINT32_MAX_VALUE).maxBitSize).toBe(32);
|
||||||
|
expect(Integer.signed(UINT32_MAX_VALUE).maxBitSize).toBe(64);
|
||||||
|
})
|
||||||
|
|
||||||
it('converts signed to unsigned and vice versa', () => {
|
it('converts signed to unsigned and vice versa', () => {
|
||||||
const n1 = new Integer(-1, 8);
|
const n1 = new Integer(-1, 8);
|
||||||
@@ -21,7 +31,14 @@ it('convers to different type', () => {
|
|||||||
|
|
||||||
const n = src.convertTo(dest);
|
const n = src.convertTo(dest);
|
||||||
expect(n.num()).toBe(UINT32_MAX_VALUE);
|
expect(n.num()).toBe(UINT32_MAX_VALUE);
|
||||||
})
|
});
|
||||||
|
|
||||||
|
it('doesnt modify 64-bit number when converting from usinged to signed', () => {
|
||||||
|
|
||||||
|
expect(Integer.unsigned(4294967295).toSigned().num()).toBe(-1);
|
||||||
|
expect(Integer.unsigned(2147483647).toSigned().num()).toBe(2147483647);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
it('converts to largest size', () => {
|
it('converts to largest size', () => {
|
||||||
const n8 = new Integer(-1, 8);
|
const n8 = new Integer(-1, 8);
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { type } from "os";
|
import { type } from "os";
|
||||||
import { INT32_MAX_VALUE, INT32_MIN_VALUE } from "./const";
|
import { INT32_MAX_VALUE, INT32_MIN_VALUE, UINT32_MAX_VALUE } from "./const";
|
||||||
import { asIntN } from "./utils";
|
import { asIntN, logLines as ln } from "./utils";
|
||||||
import formatter from "./formatter";
|
import formatter from "./formatter";
|
||||||
import calc from "./calc";
|
import calc from "./calc";
|
||||||
|
|
||||||
export type JsNumber = number | bigint;
|
export type JsNumber = number | bigint;
|
||||||
|
export type IntegerInput = JsNumber | string;
|
||||||
|
|
||||||
export class Integer {
|
export class Integer {
|
||||||
|
|
||||||
@@ -13,29 +13,37 @@ export class Integer {
|
|||||||
readonly maxBitSize: number;
|
readonly maxBitSize: number;
|
||||||
readonly signed: boolean;
|
readonly signed: boolean;
|
||||||
|
|
||||||
constructor(value: JsNumber, maxBitSize?: number, signed? : boolean) {
|
constructor(value: IntegerInput, maxBitSize?: number, signed? : boolean) {
|
||||||
|
|
||||||
this.value = typeof value == "bigint" ? value : BigInt(value);
|
this.value = typeof value == "bigint" ? value : BigInt(value);
|
||||||
this.maxBitSize = maxBitSize != null ? maxBitSize : (value >= INT32_MIN_VALUE && value <= INT32_MAX_VALUE) ? 32 : 64;
|
|
||||||
this.signed = signed == null ? true : signed == true;
|
this.signed = signed == null ? true : signed == true;
|
||||||
|
this.maxBitSize = maxBitSize != null ? maxBitSize : detectSize(this.value, this.signed);
|
||||||
|
|
||||||
|
if(!this.signed && this.value < 0)
|
||||||
|
throw new Error("Value " + value + " cannot be negative if the type is unsigned");
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned(value : JsNumber, maxBitSize?: number) {
|
static unsigned(value : IntegerInput, maxBitSize?: number) {
|
||||||
return new Integer(value, maxBitSize, false);
|
return new Integer(value, maxBitSize, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static long(value: JsNumber) : Integer {
|
static signed(value : IntegerInput, maxBitSize?: number) {
|
||||||
|
return new Integer(value, maxBitSize, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static long(value: IntegerInput) : Integer {
|
||||||
return new Integer(value, 64);
|
return new Integer(value, 64);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int(value: JsNumber) : Integer {
|
static int(value: IntegerInput) : Integer {
|
||||||
return new Integer(value, 32);
|
return new Integer(value, 32);
|
||||||
}
|
}
|
||||||
|
|
||||||
static short(value: JsNumber) : Integer {
|
static short(value: IntegerInput) : Integer {
|
||||||
return new Integer(value, 16);
|
return new Integer(value, 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
static byte(value: JsNumber) : Integer {
|
static byte(value: IntegerInput) : Integer {
|
||||||
return new Integer(value, 8);
|
return new Integer(value, 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,10 +59,13 @@ export class Integer {
|
|||||||
if(this.signed)
|
if(this.signed)
|
||||||
return new Integer(this.value, this.maxBitSize, this.signed);
|
return new Integer(this.value, this.maxBitSize, this.signed);
|
||||||
|
|
||||||
const bin = calc.engine.applyTwosComplement(this.toString(2));
|
const orig = this.toString(2).padStart(this.maxBitSize, '0');
|
||||||
const n = BigInt("0b"+bin);
|
|
||||||
|
|
||||||
return new Integer(bin[0] == '1' ? n : -n, this.maxBitSize, true)
|
const inverted = orig[0] == '1' ? calc.engine.applyTwosComplement(orig) : orig;
|
||||||
|
const n = BigInt("0b"+inverted);
|
||||||
|
const negative = orig[0] == '1';
|
||||||
|
|
||||||
|
return new Integer(negative ? -n : n, this.maxBitSize, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
resize(newSize: number) {
|
resize(newSize: number) {
|
||||||
@@ -115,4 +126,12 @@ export function asInteger(num: JsNumber | Integer | string): Integer {
|
|||||||
|
|
||||||
export function isInteger(num: JsNumber | Integer): num is Integer {
|
export function isInteger(num: JsNumber | Integer): num is Integer {
|
||||||
return (<Integer>num).maxBitSize !== undefined;
|
return (<Integer>num).maxBitSize !== undefined;
|
||||||
}
|
}
|
||||||
|
function detectSize(value: bigint, signed: boolean): number {
|
||||||
|
|
||||||
|
if(!signed)
|
||||||
|
return value > UINT32_MAX_VALUE ? 64 : 32;
|
||||||
|
else
|
||||||
|
return value < INT32_MIN_VALUE || value > INT32_MAX_VALUE ? 64 : 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -247,7 +247,6 @@ function nextPowOfTwo(num: number) : number {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function equalizeSize(n1: Integer, n2: Integer) : [Integer, Integer] {
|
function equalizeSize(n1: Integer, n2: Integer) : [Integer, Integer] {
|
||||||
console.log('equalizeSize()', new Error().stack);
|
|
||||||
if(n1.maxBitSize == n2.maxBitSize)
|
if(n1.maxBitSize == n2.maxBitSize)
|
||||||
{
|
{
|
||||||
if(n1.signed === n2.signed) return [n1,n2];
|
if(n1.signed === n2.signed) return [n1,n2];
|
||||||
|
|||||||
@@ -26,4 +26,8 @@ function randomBool() {
|
|||||||
return random(1, 10000) % 2 == 0;
|
return random(1, 10000) % 2 == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
export {chunkifyString, asIntN, random, randomBool};
|
function logLines(...params: any[]) {
|
||||||
|
console.log(params.join('\n'))
|
||||||
|
}
|
||||||
|
|
||||||
|
export {chunkifyString, asIntN, random, randomBool, logLines};
|
||||||
@@ -58,9 +58,7 @@ function applyOperator(op1 : Operand, operator: string, op2 : Operand) : Operand
|
|||||||
equalizeSize(op1, op2);
|
equalizeSize(op1, op2);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(op1.value, operator, op2.value);
|
|
||||||
const result = calc.operation(op1.value, operator, op2.value);
|
const result = calc.operation(op1.value, operator, op2.value);
|
||||||
console.log('=', result);
|
|
||||||
return new Operand(result, op2.base);
|
return new Operand(result, op2.base);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user