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

Right shift operator assert example fails on web #5020

Closed
ghost opened this issue Jun 30, 2023 · 6 comments · Fixed by #5033
Closed

Right shift operator assert example fails on web #5020

ghost opened this issue Jun 30, 2023 · 6 comments · Fixed by #5033
Assignees
Labels
cl.fixed Issue is closed as fixed e1-hours Can complete in < 8 hours of normal, not dedicated, work p2-medium Necessary but not urgent concern. Resolve when possible. target.web Target apps on the web platform

Comments

@ghost
Copy link

ghost commented Jun 30, 2023

Page URL

https://dart.dev/language/operators.html

Page source

https://github.com/dart-lang/site-www/tree/main/src/language/operators.md

Describe the problem

The assertion assert((-value >> 4) == -0x03); in the Bitwise and shift operators section of the page fails.

Expected fix

No response

Additional context

No response

@danagbemava-nc danagbemava-nc added the st.triage.triage-team Triage team reviewing and categorizing the issue label Jun 30, 2023
@danagbemava-nc
Copy link

Hi @iehrais, thanks for filing the issue. Does the assertion fail with a message that you can share? What platform did you run this on?

@ghost
Copy link
Author

ghost commented Jun 30, 2023

Hello @danagbemava-nc . There is just a message Uncaught Error: Assertion failed due to an expression (-value >> 4) == -0x03 evaluates to false with value equals to 0x22;.
I ran this in DartPad (Flutter 3.10.5 Dart SDK 3.0.5). I got the same error in DartPad (Flutter 3.12.0-3.0.pre Dart SDK 3.1.0-171.0.dev) as well.

@parlough parlough added p3-low Valid but not urgent concern. Resolve when possible. Encourage upvote to surface. act.wait-for-customer Needs response from customer e1-hours Can complete in < 8 hours of normal, not dedicated, work target.web Target apps on the web platform and removed st.triage.triage-team Triage team reviewing and categorizing the issue labels Jul 1, 2023
@parlough
Copy link
Member

parlough commented Jul 1, 2023

Thanks for opening an issue! Trying on native compilers, it seems to work as expected, so it might be due to a difference on how Dart represents numbers on the web.

However, writing equivalent code in JavaScript seems to match Dart native, so I'm not sure.

JS:

const value = 0x22;

console.log(-value >> 4); // Prints -3 (matching Dart native)

Dart (web compilers):

void main() {
  final value = 0x22;
  print((-value >> 4)); // Prints 4294967293 on web, -3 on native
}

@sigmundch Is this difference in behavior expected? If so, we probably can just switch to another example that has the same behavior on all platforms.

@parlough parlough changed the title [PAGE ISSUE]: 'Operators' Right shift operator assert example fails on web Jul 1, 2023
@eernstg
Copy link
Member

eernstg commented Jul 3, 2023

This is indeed caused by the JavaScript-like number semantics on the web, cf. https://dart.dev/guides/language/numbers#bitwise-operations.

I think it would make sense to add a comment to that example about the special treatment of numbers on the web.

Here's some background info. The following variant of the example succeeds in DartPad:

void main() {
  final value = 0x22;
  final bitmask = 0x0f;

  assert((value & bitmask) == 0x02); // AND
  assert((value & ~bitmask) == 0x20); // AND NOT
  assert((value | bitmask) == 0x2f); // OR
  assert((value ^ bitmask) == 0x2d); // XOR
  assert((value << 4) == 0x220); // Shift left
  assert((value >> 4) == 0x02); // Shift right
  assert((value >>> 4) == 0x02); // Unsigned shift right
  assert((-value >> 4).toSigned(32) == -0x03); // Shift right
  assert((-value >>> 4) > 0); // Unsigned shift right
}

The point is that bitwise operators in Dart-on-the-web will consider their operands to be 32 bit unsigned values, perform the operation in the standard manner according to that interpretation of the bits, and return a value with the same interpretation. For example -1 >> 0 will yield 4294967295, because -1 yields 4294967295 (or 0xFFFFFFFF) when it is considered to be a 32 bit unsigned number (which means that we throw away all those higher bits whose value is also 1, including the sign bit). >> 0 does nothing, so we still have 0xFFFFFFFF. If we use that bit pattern directly as a Dart int value then we get 4294967295, which is surprisingly different from -1. Similarly, -value >> 4 in the example yields 4294967293 (or 0xFFFFFFFD), which is different from -0x03.

It is not very convenient to write completely portable code using bitwise operations, but one step is simple and useful: When using a Dart-on-the-web result returned by a bitwise operator, use .toSigned(32) in order sign extend it through all the bits that were masked off during this operation.

The reason why it does not create any issues in all the other cases is that they yield a value that doesn't have a 1 in bit 31 (the sign bit for a 32 bit signed number), so they keep their value when reinterpreted from 32 bit unsigned to JavaScript numbers (where the 32 bit bitvector is considered as part of an IEEE 754 value). So if you know for certain that the result will be one of those bit patterns whose toSigned(32) is a no-op, you can just compute the bitwise operations and use them without further steps. For instance, if value and bitmask are small numbers then value & bitmask will just work. One rule of thumb is "on the web, be careful with negative numbers and bit operations!"

@sigmundch, I believe you gave some further background information about the reason why the Dart-on-the-web behavior isn't exactly the same as the JavaScript behavior with bit operations? I can't find it right now, but it would have been a very similar discussion, presumably in the SDK repo. As I remember it, you said that the experience during the early years of Dart was that the given semantics was the least surprising in practice.

@parlough parlough added p2-medium Necessary but not urgent concern. Resolve when possible. and removed p3-low Valid but not urgent concern. Resolve when possible. Encourage upvote to surface. act.wait-for-customer Needs response from customer labels Jul 3, 2023
@parlough
Copy link
Member

parlough commented Jul 3, 2023

Thanks for the great explanation @eernstg! That makes a lot of sense! I completely missed the 32-bit unsigned treatment of the operands on web when checking the page.

I think you’re right, it would be valuable to briefly mention the number semantics here, link to that section of the number semantics doc, and keep the mentioned example with a note.

@sigmundch
Copy link
Member

Thanks @eernstg for the great and detailed answer! I can't tell for sure, but I think the historical reference you are thinking of is the one that @lrhn wrote here: dart-lang/sdk#49019 (comment)

atsansone pushed a commit that referenced this issue Jul 14, 2023
I didn't want to repeat too much of what was covered on the number
semantics page or focus too much on the platform differences, so I did
my best to find a minimal balance. Let me know what you think.

If we want to cover more of what @eernstg mentioned in
#5020 (comment),
such as potential workarounds, that would be best as a follow-up
expansion on the number semantics page rather than here.

**Staged:**
https://dart-dev--pr5033-fix-5020-spu2i1id.web.app/language/operators#bitwise-and-shift-operators

Closes #5020
@danagbemava-nc danagbemava-nc added the cl.fixed Issue is closed as fixed label Jul 17, 2023
rmacnak-google pushed a commit to rmacnak-google/site-www that referenced this issue Sep 5, 2023
I didn't want to repeat too much of what was covered on the number
semantics page or focus too much on the platform differences, so I did
my best to find a minimal balance. Let me know what you think.

If we want to cover more of what @eernstg mentioned in
dart-lang#5020 (comment),
such as potential workarounds, that would be best as a follow-up
expansion on the number semantics page rather than here.

**Staged:**
https://dart-dev--pr5033-fix-5020-spu2i1id.web.app/language/operators#bitwise-and-shift-operators

Closes dart-lang#5020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cl.fixed Issue is closed as fixed e1-hours Can complete in < 8 hours of normal, not dedicated, work p2-medium Necessary but not urgent concern. Resolve when possible. target.web Target apps on the web platform
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants