Pergunta

I'm doing ZigZag encoding on 32bit integers with Dart. This is the source code that I'm using:

int _encodeZigZag(int instance) => (instance << 1) ^ (instance >> 31);
int _decodeZigZag(int instance) => (instance >> 1) ^ (-(instance & 1));

The code works as expected in the DartVM.

But in dart2js the _decodeZigZag function is returning invalid results if I input negativ numbers. For example -10. -10 is encoded to 19 and should be decoded back to -10, but it is decoded to 4294967286. If I run (instance >> 1) ^ (-(instance & 1)) in the JavaScript console of Chrome, I get the expected result of -10. That means for me, that Javascript should be able to run this operation properly with it number model.

But Dart2Js generate the following JavaScript, that looks different from the code I tested in the console:

return ($.JSNumber_methods.$shr(instance, 1) ^ -(instance & 1)) >>> 0;

Why does Dart2Js adds a usinged right shift by 0 to the function? Without the shift, the result would be as expected.

Now I'm wondering, is it a bug in the Dart2Js compiler or the expected result? Is there a way to force Dart2Js to output the right javascript code?

Or is my Dart code wrong?

PS: Also tested splitting up the XOR into other operations, but Dart2Js is still adding the right shift:

final a = -(instance & 1);
final b = (instance >> 1);

return (a & -b) | (-a & b);

Results in:

a = -(instance & 1);
b = $.JSNumber_methods.$shr(instance, 1);
return (a & -b | -a & b) >>> 0;
Foi útil?

Solução

For efficiency reasons dart2js compiles Dart numbers to JS numbers. JS, however, only provides one number type: doubles. Furthermore bit-operations in JS are always truncated to 32 bits.

In many cases (like cryptography) it is easier to deal with unsigned 32 bits, so dart2js compiles bit-operations so that their result is an unsigned 32 bit number.

Neither choice (signed or unsigned) is perfect. Initially dart2js compiled to signed 32 bits, and was only changed when we tripped over it too frequently. As your code demonstrate, this doesn't remove the problem, just shifts it to different (hopefully less frequent) use-cases.

Non-compliant number semantics have been a long-standing bug in dart2js, but fixing it will take time and potentially slow down the resulting code. In the short-term future Dart developers (compiling to JS) need to know about this restriction and work around it.

Outras dicas

Looks like I found equivalent code that output the right result. The unit test pass for both the dart vm and dart2js and I will use it for now.

int _decodeZigZag(int instance) => ((instance & 1) == 1 ? -(instance >> 1) - 1 : (instance >> 1));

Dart2Js is not adding a shift this time. I would still be interested into the reason for this behavior.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top