Home > Software engineering >  Javascript fails to calculate xorshiftmult correctly. How to make it work?
Javascript fails to calculate xorshiftmult correctly. How to make it work?

Time:10-31

I'm trying to do the same math as in statically typed languages, but it gives me different numbers if the argument is big.

function xorshiftmult(x) {
    x ^= x >> 12n
    x ^= x << 25n
    x ^= x >> 27n
    return BigInt.asUintN(64, x * 2685821657736338717n)
}

Small numbers:

input/output

1n 5180492295206395165n
2n 10360984590412790330n
3n 15541476885619185495n
4n 4961046764852367761n
5n 4769895744586085492n
6n 15322031355265158091n
7n 15130880334998875822n
8n 9922093529704735522n
9n 15102585824911130687n

Big numbers:

input/output

7292821753470094447n 4831377277631613306n
17517393223776413492n 8648459866934146562n
12871125787594255668n 10283147200615164616n
16549285201548370653n 6000770026373893856n
18260228909704403279n 13976674343699112182n
7574594482456054693n 16722430591553842838n
17620850200451425087n 15710702043522419077n
11252008783195621033n 12240320011096546170n
13791874522026752862n 7426906761295174006n
12869035004249813321n 1654853426672194495n

(golang)

func xorshiftmult(x uint64) uint64 {
    x ^= x >> 12
    x ^= x << 25
    x ^= x >> 27
    return x * 2685821657736338717
}

Small numbers:

input/output

1 5180492295206395165
2 10360984590412790330
3 15541476885619185495
4 4961046764852367761
5 4769895744586085492
6 15322031355265158091
7 15130880334998875822
8 9922093529704735522
9 15102585824911130687

BIG numbers:

input/output

7292821753470094447 4375439080089995642
17517393223776413492 3385204219959375362
12871125787594255668 13047519269082376904
16549285201548370653 13622873389358370528
18260228909704403279 10325503129038119158
7574594482456054693 7461249648354733718
17620850200451425087 13330604890916407685
11252008783195621033 8666328877708335994
13791874522026752862 16881109032334662006
12869035004249813321 14233246107464520639

As you can see, big numbers are different. But why? What I did wrong?

CodePudding user response:

You need to clamp the number at every step, not just at the end. Otherwise, bits you shifted to the left beyond the 64th bit will later come back in when you shift right again, while in Go they would have "fallen off the cliff" already.

For example if your input is 2^50, the first step would give you 2^50 2^38 and the second step then 2^50 2^38 2^75 2^63 (while in Go you would get just 2^50 2^38 2^63 at that point), so after the third step you'd get 2^50 2^38 2^75 2^63 2^23 2^11 2^48 2^36 (while in Go the 2^75 and 2^48 wouldn't exist) and after clamping the final result is 2^50 2^38 2^63 2^23 2^11 2^48 2^36 - in Go the 2^48 wouldn't be there because it comes from 2^75 in the second step, which in Go would already have been clamped away and couldn't have introduced the 2^48 in the next step.

function xorshiftmult(x) {
    x = BigInt.asUintN(64, x ^ x >> 12n)
    x = BigInt.asUintN(64, x ^ x << 25n) 
    x = BigInt.asUintN(64, x ^ x >> 27n)
    return x
} 
  • Related