Хотел написать код на JavaScript, выполняющий то же самое что уже написанный на C++ код. Столкнулся с очень многими нюансами, один из которых это разница в вычитании (и других арифметических операциях) UNSIGNED INT типов.
Например, в С/С++ подобных языках в случае если переменные INT объявлены unsigned.
1 - 115 = 4294967182
std::cout << "1-115: " + std::to_string((unsigned int)1-(unsigned int)115) + "\n"; //выведет: 4294967182
При попытке повторить то же самое на javascript. На javascript результатом вычитания будет:
1-115
-114 или же 114 если взять abs.
С использованием bigInt библиотеки (или без нее) будет 114 (ну или минус 114 если взять abs).
Собственно, моя цель научить javascript работать с unsigned int и при вычитании получать точно такое же значение как получаю в C++ : 4294967182:
с = new bigInt(1); a = new bigInt(115); c = c.minus(a).abs(); console.log(c) //114
https://www.quora.com/Can-a-subtraction-give-a-negative-result-using-unsigned-number очень полезная статья, объясняющая почему так происходит.
Операции с UNSGINED INT в javascript — представить число в виде байтов.
Unsigned INT никогда не могут быть негативными. Если размерность числа достигает своего предела, то, как часы, отсчет продолжается с нуля.
Демонстрация переполнения на примере C++ кода:
unsigned int UINT_MAX = 4294967295; // Maximum value for a variable of type unsigned int. 4294967295 (0xffffffff) std::cout << UINT_MAX + 1; // will print 0 std::cout << UINT_MAX + 2; // will print 1
Чтобы проводить операции с 4х-байтовыми unsigned INT на javascript, давайте сначала представим каждое число в виде массива из четырех байт. Каждый из 4х байт мы можем получить как:
- (n1 >> 24) & 255
- (n1 >> 16) & 255
- (n1 >> 8) & 255
- (n1 >> 0) & 255
Выше валидный javascript синтаксис, используются операторы:
-
>>
— right shift operator -
&
— bitwise AND
const n1 = 1 //initial number 1 const n2 = 115 //initial number 2 const bytes1 = [(n1 >> 24) & 255, (n1 >> 16) & 255, (n1 >> 8) & 255, (n1 >> 0) & 255]; const bytes2 = [(n2 >> 24) & 255, (n2 >> 16) & 255, (n2 >> 8) & 255, (n2 >> 0) & 255]; //lets calculate n1 - n2 const res = minus(bytes1, bytes2); console.log('Result as array of 4 bytes', res); // обратное преобразование байт в число console.log('Final subtraction result as number'); console.log((res[0] << 24) + (res[1] << 16) + (res[2] << 8) + (res[3] << 0)); // -114 yeah!
Единственное, чего тут не хватает, это реализации функции minus для [4]byte. Функцию вычитания, принимающая аргументы как массивы байт, может выглядеть вот так:
function minus(a, b) { let sub = 0; for (let i = 3; i >= 0; i --) { a[i] -= b[i] + sub; sub = 0; if (a[i] < 0) { a[i] += 256; sub = 1; } } return a; }
Данный код выполнит обратное преобразование байт в число с использованием left shift (<<
) оператора:
(res[0] << 24) + (res[1] << 16) + (res[2] << 8) + (res[3] << 0)
UPD: нужно закончить статью.
Комментарии 0