Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implementation issue for unsigned/triple-shift >>> operator #478

Closed
6 tasks done
floitschG opened this issue Sep 26, 2017 · 24 comments
Closed
6 tasks done

Implementation issue for unsigned/triple-shift >>> operator #478

floitschG opened this issue Sep 26, 2017 · 24 comments
Labels
implementation Track the implementation of a specific feature OBSOLETE: Please use SDK issue

Comments

@floitschG
Copy link

floitschG commented Sep 26, 2017

This is the implementation issue for the triple-shift >>> operator (unsigned shift), see language feature #120

The operator has the same precedence as >>.
The expression e1 >>> e2 is a compile-time constant expression if both e1 and e2 are compile-time constant expressions evaluating to integers (and int will implement the operator).
The symbol #>>> is valid (along with const Symbol(">>>")), as is x >>>= e as an expression.

Adding the new operator is non-breaking.

@rashedmyt
Copy link
Contributor

waiting for this operator to be in release

@kevmoo
Copy link
Member

kevmoo commented Jul 25, 2019

@lrhn what's the story with this?

@aadilmaan @mit-mit – are we tracking this somewhere?

Related: https://stackoverflow.com/questions/57190508/does-dart-have-operator/57210971

@mit-mit
Copy link
Member

mit-mit commented Jul 29, 2019

This is now tracked in #120. Closing the present historical issue.

@mit-mit mit-mit closed this as completed Jul 29, 2019
@mit-mit mit-mit transferred this issue from dart-lang/sdk Jul 30, 2019
@mit-mit mit-mit added the implementation Track the implementation of a specific feature OBSOLETE: Please use SDK issue label Jul 30, 2019
@mit-mit mit-mit changed the title Add unsigned shift operator. Implementation issue for unsigned/triple-shift >>> operator Jul 30, 2019
@mit-mit
Copy link
Member

mit-mit commented Jul 30, 2019

Transferring and re-purposing to be the implementation issue for this feature. Please note that this isn't currently being implemented, it's just ready for implementation.

@mit-mit mit-mit reopened this Jul 30, 2019
@AKushWarrior
Copy link

Is there a status on this? It would be massively helpful for cryptography.

@AKushWarrior
Copy link

AKushWarrior commented May 20, 2020

I don't think this would be too difficult. I haven't tried forking dart's internals, but I'm using a utility extension function. Converted to an operator, it would look like. (EDIT: JS is hard)

int operator >>> (int n) {
    return ((this >= 0) ? this >> (n) : ~(~this >> (n)));
}

@AKushWarrior
Copy link

AKushWarrior commented May 22, 2020

The code snippet I posted was wrong. I fixed it.

It's tested and works on the VM, but does not work when trans-piled to JS.

@AKushWarrior
Copy link

AKushWarrior commented May 22, 2020

New code snippet, which works both on the VM and when trans-piled to JS. (EDIT: it doesn't; it's broken for JS for negative numbers; see below)

  int operator >>> (int count) {
    if (!isJS) {
      return (this >= 0) ? this >> count : ~(~this >> count);
    } else {
      count &= 0x1f;
      if (this >= 0) {
        return (this >> count + 1);
      } else {
        var mask = 1 << (31 - count);
        return (~this >> count) | mask;
      }
    }
  }

  bool get isJS {
    // Some definition here...
  }

I dunno about performance, but I use only bitwise operators, so it should be decent. It's tested on the VM and DartPad. All cases (negative, zero, positive) check out for both.

If someone could guide me through the process, I'd be happy to try and implement the >>> operator as a language feature.

@mpfaff
Copy link

mpfaff commented Jul 25, 2020

@AKushWarrior what is x supposed to be in your code example?

@AKushWarrior
Copy link

@AKushWarrior what is x supposed to be in your code example?

Nothing. It was a remnant from me testing the function in a different setup than the extension method presented here. It should be fixed now.

@mpfaff
Copy link

mpfaff commented Jul 25, 2020

Neither quite works for me.

This is the code I'm testing:

(3185753779 * 3185753779).tripleShift(32)

On DartPad, I get 1208817664, which is half of 2417635328 which I get from a pure JavaScript >>>. On the Dart VM, I get -1931962775.

@AKushWarrior
Copy link

@mpfaff I'll review at some point. I'm kinda swamped and I didn't really test it super extensively, just with 10 or so test cases.

@AKushWarrior
Copy link

AKushWarrior commented Jul 27, 2020

@mpfaff new DartPad code is below and seems to work fine on DartPad. If you could try to break it with more cases, that would be great. All test cases have been generated using TypeScript.

void main() {
  //positive
  print((3185753779 * 3185753779).tripleShift(32) == 2417635328);
  print(25.tripleShift(25) == 0);
  print(13123423.tripleShift(5) == 410106);
  
  //zero
  print(0.tripleShift(23) == 0);
  
  //negative
  print((-12245234).tripleShift(11) == 2091172);
  print((-2938648).tripleShift(4) == 268251790);
}

extension tripShift on int {
  int tripleShift (int count) {
    if (false) {
      return (this >= 0) ? this >> count : ~(~this >> count);
    } else {
      // assumes this > -4294967295, or -(2^32 -1)
      count &= 0x1f;
      if (this >= 0) {
        return (this >> count);
      } else {
        return (this >> count) ^ ((0xFFFFFFFF) ^ ((1 << (32-count)) - 1));
      }
    }
  }
}

@AKushWarrior
Copy link

AKushWarrior commented Jul 27, 2020

There should be a difference between how zero-fill right shift works on the web and on the VM: 64-bit numbers which have been zero-fill right shifted are very different from 32-bit numbers which have been zero-fill right shifted.

@AKushWarrior
Copy link

AKushWarrior commented Jul 27, 2020

@mit-mit @lrhn is this on the roadmap for post-NNBD improvements?

@eernstg
Copy link
Member

eernstg commented Aug 4, 2020

It is on a shortlist of small, useful features: #1077.

@Comevius
Copy link

@AKushWarrior

Neither of your implementations work properly. Many of your tests also expects the wrong result:

(3185753779 * 3185753779) == -8297716933296770775;
// 1000110011011000100101100110100110010000000110100011000100101001
(3185753779 * 3185753779) >>> 32 == 2363004521;
// 10001100110110001001011001101001
(3185753779 * 3185753779) >>> 32 != 2417635328;
// 10010000000110100011000000000000

This is the correct implementation for Dart Native:

extension TripleShift on int {
  int tripleShift(int count) { 
    return (this >> count) & ~(-1 << (64 - count));
  }
}

@AKushWarrior
Copy link

@AKushWarrior

Neither of your implementations work properly. Many of your tests also expects the wrong result:

(3185753779 * 3185753779) == -8297716933296770775;
// 1000110011011000100101100110100110010000000110100011000100101001
(3185753779 * 3185753779) >>> 32 == 2363004521;
// 10001100110110001001011001101001
(3185753779 * 3185753779) >>> 32 != 2417635328;
// 10010000000110100011000000000000

This is the correct implementation for Dart Native:

extension TripleShift on int {
  int tripleShift(int count) { 
    return (this >> count) & ~(-1 << (64 - count));
  }
}

I'm matching the JS version. While you are mathematically right, a programmer is likely to be confused that using >>> in dart2js results in a different value than pure js.

@Comevius
Copy link

@AKushWarrior

JavaScript only defines bitwise operations on 32-bit unsigned integers, so even when you do >>>0 it lowers the precision of your already lowered precision 53-bit int to the 0 to 0xffffffff range.

You would need something like Int64 to emulate 64-bit values and operations:
https://pub.dev/documentation/fixnum/latest/fixnum/Int64/shiftRightUnsigned.html

BigInt would be another solution, but Dart is yet to support it.

@AKushWarrior
Copy link

AKushWarrior commented Aug 12, 2020

@Comevius

Dart integers, when translated to JS, are 32 bit, not 53 bit. While I agree that we should have clearer types (or just standardize the fixnum types), that would break all existing code.

When using Dart for JS, you have to assume that all your integers are 32 bit, or just use https://pub.dev/packages/fixnum. Dart integers, to the best of my knowledge, are not meant to emulate 64 bit integers when transpiled to JS.

Instead of trying to create a solution that produces the same results on both Native and JS, which doesn't make sense given the nature of both platforms, I created a separate version which matches the ES6 results for >>>. All those test cases were designed to be run on DartPad, which transpiles code to JS before running it.

EDIT: In fact, your native code is not platform agnostic either. If you evaluate that code after transpilation to JS, it will result in incorrect shifts (due to 32 bit truncation).

@eernstg
Copy link
Member

eernstg commented Aug 12, 2020

Dart integers, when translated to JS, are 32 bit, not 53 bit.

Not quite: Dart numbers are represented as JavaScript numbers when compiled to JavaScript. A number is int if it has no fractional part, and it always is double. So that yields 53 bits of information for an int, but includes larger numbers where the "missing" low bits are treated as zeros (so 18014398509481982 is int).

However, bitwise operations work on 32 bits and treat them as an unsigned number (cf. this comment). So >>> should work accordingly when compiled to JS, and in particular there will be differences between the results when the target language is native vs. when it is JS.

@AKushWarrior
Copy link

Dart integers, when translated to JS, are 32 bit, not 53 bit.

Not quite: Dart numbers are represented as JavaScript numbers when compiled to JavaScript. A number is int if it has no fractional part, and it always is double. So that yields 53 bits of information for an int, but includes larger numbers where the "missing" low bits are treated as zeros (so 18014398509481982 is int).

Interesting. The only time I considered the bit length of integers was when doing bitwise ops, so I guess it never occurred to me.

However, bitwise operations work on 32 bits and treat them as an unsigned number (cf. this comment). So >>> should work accordingly when compiled to JS, and in particular there will be differences between the results when the target language is native vs. when it is JS.

This is in line with my example. My newest DartPad example works with expected results from the target language (JS).

@srawlins
Copy link
Member

srawlins commented Feb 9, 2021

Note: added analyzer subtask above dart-lang/sdk#44908

@eernstg
Copy link
Member

eernstg commented Jul 29, 2021

Closing: This feature was enabled by default in 2.14.

@eernstg eernstg closed this as completed Jul 29, 2021
dart-bot pushed a commit to dart-lang/sdk that referenced this issue Sep 15, 2021
The unsigned right shift was [implemented](dart-lang/language#478 (comment))

Closes #47227
#47227

GitOrigin-RevId: ec84d68
Change-Id: Iafc195c8df212c8f78330f6ebef230daa6a6d280
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/213560
Reviewed-by: Kevin Moore <[email protected]>
Commit-Queue: Kevin Moore <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
implementation Track the implementation of a specific feature OBSOLETE: Please use SDK issue
Projects
None yet
Development

No branches or pull requests

9 participants