I am trying to port over a Javascript hashing function to Swift:
In Javascript:
68207947269 ^ 51 = -511529418
where as the same calculation in Swift 5
68207947269 ^ 51 = 68207947318
How, why are they different?
Edit, added the JS hashing function
function hash(str) {
var hash = 5381,
i = str.length;
while(i) {
hash = (hash * 33) ^ str.charCodeAt(--i);
}
/* JavaScript does bitwise operations (like XOR, above) on 32-bit signed
* integers. Since we want the results to be always positive, convert the
* signed int to an unsigned by doing an unsigned bitshift. */
return hash >>> 0;
}
Edit 2, added current broken Swift:
extension String {
subscript(i: Int) -> String {
return String(self[index(startIndex, offsetBy: i)])
}
}
extension Character {
func unicodeScalarCodePoint() -> UInt32 {
let characterString = String(self)
let scalars = characterString.unicodeScalars
return scalars[scalars.startIndex].value
}
}
func hashString(_ string: String) -> UInt32 {
var hash: Int32 = 5381
var i = string.count - 1
while(i >= 0) {
let char = Int32(Character(string[i]).unicodeScalarCodePoint())
// Crash here with error: `Swift runtime failure: arithmetic overflow`
let hashMultiply = Int32(truncating: NSNumber(value: hash * 33))
hash = hashMultiply ^ char
i -= 1
}
return UInt32(bitPattern: hash)
}
hashString("0x8f1083db77b5F556E46Ac46A29DE86e01031Bb14")
According to here, bitwise operators in JavaScript use 32-bit operands. 68207947269 is too large to be represented in 32 bits, so it gets truncated first automatically, then the bitwise operation is carried out.
Swift integer literals are of type Int by default, and the size of Int is platform-dependent. It is most likely 64-bits for you, which is why the different result is produced.
To produce the same result as the JavaScript code, convert it to Int32 by truncating first:
Int32(truncatingIfNeeded: 68207947269) ^ 51
Note that you get a Int32 as a result. You might need to do more type conversions later on.
About your Swift translation, I see two main problems.
Firstly, hash * 33 will overflow and cause a crash. The JavaScript version doesn't need to worry about this because the result will simply "wrap around" in JavaScript. Fortunately, there is an operator that also "wraps around" if the result overflows (rather than crashing) in Swift. So you can do:
hash &* 33
Secondly, you are handling strings differently from the JavaScript version. In JavaScript, charCodeAt returns a UTF-16 code unit, but your Swift code gets the unicode scalar instead.
To get the same behaviour, you should do:
extension String.UTF16View {
subscript(i: Int) -> Element {
return self[index(startIndex, offsetBy: i)]
}
}
...
Int32(string.utf16[i])
Note that in JavaScript:
The operands are converted to 32-bit integers and expressed by a series of bits (zeroes and ones). Numbers with more than 32 bits get their most significant bits discarded.
Integer literals in Swift are of type Int by default, which is your architecture's word width. If you tried this on any recent Apple device, it'll be 64-bit.
So to mimic JavaScript behavior, you would need to cast to an Int32:
Int32(truncatingIfNeeded: 68207947269) ^ Int32(51)
which gives the desired result -511529418.
Note that your JavaScript code discards bits of the integer 68207947269 as it cannot be represented as a 32-bit integer.
Related
I am performing following operation
let a = 596873718249029632;
a ^= 454825669;
console.log(a);
Output is 454825669 but the output should have been 596873718703855301. Where I am doing wrong? What I should do to get 596873718703855301 as output?
EDIT: I am using nodejs Bigint library , my node version is 8.12.0
var bigInt = require("big-integer");
let xor = bigInt(596873718249029632).xor(454825669);
console.log(xor)
Output is
{ [Number: 596873717794203900]
value: [ 4203941, 7371779, 5968 ],
sign: false,
isSmall: false }
It is wrong. it should have been 596873718703855301.
From MDN documentation about XOR:
The operands are converted to 32-bit integers and expressed by a series of bits (zeroes and ones). Numbers with more than 32 bits get their most significant bits discarded.
Since the 32 least significant bits of 596873718249029632 are all 0, then the value of a is effectively 0 ^ 454825669, which is 454825669.
To get the intended value of 596873718703855301, BigInts can be used, which allow you to perform operations outside of the range of the Number primitive, so now your code would become:
let a = 596873718249029632n;
a ^= 454825669n;
console.log(a.toString());
In response to your edit, when working with integers and Number, you need to ensure that your values do not exceed Number.MAX_SAFE_INTEGER (equal to 253 - 1, beyond that point the double precision floating point numbers loose sufficient precision to represent integers). The following snippet worked for me:
var big_int = require("big-integer");
let xor = bigInt("596873718249029632").xor("454825669");
console.log(xor.toString());
How do you print an unsigned integer when using JavaScript's BigInt?
BigInts can be printed as binary representation using toString(2). However for negative values this function just appends a - sign when printing.
BigInt(42).toString(2)
// output => 101010
BigInt(-42).toString(2)
// output => -101010
How do I print the unsigned representation of BigInt(42)? I that with regular numbers you can do (-42 >>> 0).toString(2), however the unsigned right shift seems not to be implemented for BigInt, resulting in an error
(BigInt(-42) >>> BigInt(0)).toString(2)
// TypeError: BigInts have no unsigned right shift, use >> instead
An easy way to get the two's complement representation for negative BigInts is to use BigInt.asUintN(bit_width, bigint):
> BigInt.asUintN(64, -42n).toString(2)
'1111111111111111111111111111111111111111111111111111111111010110'
Note that:
You have to define the number of bits you want (64 in my example), there is no "natural"/automatic value for that.
Given only that string of binary digits, there is no way to tell whether this is meant to be a positive BigInt (with a value close to 2n**64n) or a two's complement representation of -42n. So if you want to reverse the conversion later, you'll have to provide this information somehow (e.g. by writing your code such that it implicitly assumes one or the other option).
Relatedly, this is not how -42n is stored internally in current browsers. (But that doesn't need to worry you, since you can create this output whenever you want/need to.)
You could achieve the same result with a subtraction: ((2n ** 64n) - 42n).toString(2) -- again, you can specify how many bits you'd like to see.
Is there something like bitAtIndex for BigInt?
No, because there is no specification for how BigInts are represented. Engines can choose to use bits in any way they want, as long as the resulting BigInts behave as the specification demands.
#Kyroath:
negative BigInts are represented as infinite-length two's complement
No, they are not: the implementations in current browsers represent BigInts as "sign + magnitude", not as two's complement. However, this is an unobservable implementation detail: implementations could change how they store BigInts internally, and BigInts would behave just the same.
What you probably meant to say is that the two's complement representation of any negative integer (big or not) is conceptually an infinite stream of 1-bits, so printing or storing that in finite space always requires defining a number of characters/bits after which the stream is simply cut off. When you have a fixed-width type, that obviously defines this cutoff point; for conceptually-unlimited BigInts, you have to define it yourself.
Here's a way to convert 64-bit BigInts into binary strings:
// take two's complement of a binary string
const twosComplement = (binaryString) => {
let complement = BigInt('0b' + binaryString.split('').map(e => e === "0" ? "1" : "0").join(''));
return decToBinary(complement + BigInt(1));
}
const decToBinary = (num) => {
let result = ""
const isNegative = num < 0;
if (isNegative) num = -num;
while (num > 0) {
result = (num % BigInt(2)) + result;
num /= BigInt(2);
}
if (result.length > 64) result = result.substring(result.length - 64);
result = result.padStart(64, "0");
if (isNegative) result = twosComplement(result);
return result;
}
console.log(decToBinary(BigInt(5))); // 0000000000000000000000000000000000000000000000000000000000000101
console.log(decToBinary(BigInt(-5))); // 1111111111111111111111111111111111111111111111111111111111111011
This code doesn't do any validation, however.
Trying some bit manipulation in javascript.
Consider the following:
const n = 4393751543811;
console.log(n.toString(2)) // '111111111100000000000000000000000000000011'
console.log(n & 0b11) // last two bits equal 3
const m = n >> 2; // right shift 2
// The unexpected.
console.log(m.toString(2)) // '0'
The result is 0? The expected output I am looking for after the right shift is:
111111111100000000000000000000000000000011 // pre
001111111111000000000000000000000000000000 // post >>
How is this accomplished?
Javascript bitwise operators on numbers work "as if" on 32bit integers.
>> (sign-propagating right-shift for numbers) will first convert to a 32-bit integer. If you read linked spec, note specifically
Let int32bit be int modulo 232.
In other words, all bits above 32 will simply be ignored. For your number, this results in the following:
111111111100000000000000000000000000000011
┗removed━┛┗━━━━━━━━━━━━━━32bit━━━━━━━━━━━━━┛
If you want, you can use BigInt:
const n = 4393751543811n; // note the n-suffix
console.log(n.toString(2))
console.log(n & 0b11n) // for BigInt, all operands must be BigInt
const m = n >> 2n;
// The expected.
console.log(m.toString(2))
The spec for >> on BigInt uses BigInt::leftShift(x, -y), where it in turn states:
Semantics here should be equivalent to a bitwise shift, treating the BigInt as an infinite length string of binary two's complement digits.
I am performing following operation
let a = 596873718249029632;
a ^= 454825669;
console.log(a);
Output is 454825669 but the output should have been 596873718703855301. Where I am doing wrong? What I should do to get 596873718703855301 as output?
EDIT: I am using nodejs Bigint library , my node version is 8.12.0
var bigInt = require("big-integer");
let xor = bigInt(596873718249029632).xor(454825669);
console.log(xor)
Output is
{ [Number: 596873717794203900]
value: [ 4203941, 7371779, 5968 ],
sign: false,
isSmall: false }
It is wrong. it should have been 596873718703855301.
From MDN documentation about XOR:
The operands are converted to 32-bit integers and expressed by a series of bits (zeroes and ones). Numbers with more than 32 bits get their most significant bits discarded.
Since the 32 least significant bits of 596873718249029632 are all 0, then the value of a is effectively 0 ^ 454825669, which is 454825669.
To get the intended value of 596873718703855301, BigInts can be used, which allow you to perform operations outside of the range of the Number primitive, so now your code would become:
let a = 596873718249029632n;
a ^= 454825669n;
console.log(a.toString());
In response to your edit, when working with integers and Number, you need to ensure that your values do not exceed Number.MAX_SAFE_INTEGER (equal to 253 - 1, beyond that point the double precision floating point numbers loose sufficient precision to represent integers). The following snippet worked for me:
var big_int = require("big-integer");
let xor = bigInt("596873718249029632").xor("454825669");
console.log(xor.toString());
I am performing bitwise operations, the result of which is apparently being stored as a two's complement number. When I hover over the variable it's stored in I see- num = -2086528968.
The binary of that number that I want is - (10000011101000100001100000111000).
But when I say num.toString(2) I get a completely different binary representation, the raw number's binary instead of the 2s comp(-1111100010111011110011111001000).
How do I get the first string back?
Link to a converter: rapidtables.com/convert/number/decimal-to-binary.html
Put in this number: -2086528968
Follow bellow the result:
var number = -2086528968;
var bin = (number >>> 0).toString(2)
//10000011101000100001100000111000
console.log(bin)
pedro already answered this, but since this is a hack and not entirely intuitive I'll explain it.
I am performing bitwise operations, the result of which is apparently being stored as a two's complement number. When I hover over the variable its stored in I see num = -2086528968
No, the result of most bit-operations is a 32bit signed integer. This means that the bit 0x80000000 is interpreted as a sign followed by 31 bits of value.
The weird bit-sequence is because of how JS stringifies the value, something like sign + Math.abs(value).toString(base);
How to deal with that? We need to tell JS to not interpret that bit as sign, but as part of the value. But how?
An easy to understand solution would be to add 0x100000000 to the negative numbers and therefore get their positive couterparts.
function print(value) {
if (value < 0) {
value += 0x100000000;
}
console.log(value.toString(2).padStart(32, 0));
}
print(-2086528968);
Another way would be to convert the lower and the upper bits seperately
function print(value) {
var signBit = value < 0 ? "1" : "0";
var valueBits = (value & 0x7FFFFFFF).toString(2);
console.log(signBit + valueBits.padStart(31, 0));
}
print(-2086528968);
//or lower and upper half of the bits:
function print2(value) {
var upperHalf = (value >> 16 & 0xFFFF).toString(2);
var lowerHalf = (value & 0xFFFF).toString(2);
console.log(upperHalf.padStart(16, 0) + lowerHalf.padStart(16, 0));
}
print2(-2086528968);
Another way involves the "hack" that pedro uses. You remember how I said that most bit-operations return an int32? There is one operation that actually returns an unsigned (32bit) interger, the so called Zero-fill right shift.
So number >>> 0 does not change the bits of the number, but the first bit is no longer interpreted as sign.
function uint32(value){
return value>>>0;
}
function print(value){
console.log(uint32(value).toString(2).padStart(32, 0));
}
print(-2086528968);
will I run this shifting code only when the number is negative, or always?
generally speaking, there is no harm in running nr >>> 0 over positive integers, but be careful not to overdo it.
Technically JS only supports Numbers, that are double values (64bit floating point values). Internally the engines also use int32 values; where possible. But no uint32 values. So when you convert your negative int32 into an uint32, the engine converts it to a double. And if you follow up with another bit operation, first thing it does is converting it back.
So it's fine to do this like when you need an actual uint32 value, like to print the bits here, but you should avoid this conversion between operations. Like "just to fix it".