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
Some UInt calculations aren't unsigned #2736
Comments
@ncannasse: Is this specified? |
Not-static platforms such as JS or Neko will turn Int/UInt overflowed operations into Float. The value is still an integer since it has % 1 == 0, but it might lose precision. Neko is particular in the sense that all operations including * on two Int are overflowing on 32 bits, whereas for JS it's only bitwise operators such as ^ | & >> >>> << However that should not change the result of UInt operations since the abstract is meant to deal with these platform specific things. |
I think the problem here is that the first UInt is assigned by an Int that has already overflowed. If this can't be reproduced by (50000:UInt) * (50000:UInt) then we're all green, since Int overflow is unspecified |
|
|
Then it should be fixed for UInt at least |
BTW we should maybe translate >> into >>> for UInt, but that will introduce a regression |
I'm quite sure that fixing this would require basing UInt on Int32. |
I've got a branch that has UInt implemented with Int32 and fixes the other issues with div and mod. I have two remaining questions:
|
Additionally, abstract UInt lacks class MainTest {
static public function main() {
var a:UInt = (50000:UInt) * (50000:UInt);
trace(a > 2500000000.0); //false
trace(a < 2500000000.0); //false
trace(a == 2500000000.0); //false
}
} So, you should add below functions to UInt. @:commutative @:op(A == B) private static function equalsFloat(a:UInt, b:Float):Bool {
return a.toFloat() == b;
}
@:commutative @:op(A != B) private static function notEqualsFloat(a:UInt, b:Float):Bool {
return a.toFloat() != b;
} |
@ncannasse: so is our UInt type actually more of a UInt32 type? |
@Simn, that's an interesting question. I didn't get much thoughts to UInt since the start, since it was to me more like a Flash specific thing. Now that you mention it, I don't think that UInt overflow rules should actually differ from Int overflow ones, but it's quite complex since their domain definition is not the same, and per-platform auto promotion from Int to Number (JS,Flash) in case of overflow is also an issue @Herschel what would you suggest if we were not to use Int32 ? |
We should address this for 3.1.1 one way or another. |
If we don't use Int32 and ignore overflow behavior, I can still fix the issues with div and mod, and add the Float comparisons that @shohei909 suggested. There's still the question of allowing Int == UInt comparisons, as well as shifts in flash9. (It affects left shifts too, btw, because Flash VM defines shifts to always return |
@Herschel I think we should disalow Int=UInt comparisons, since they might cause some issues with negative numbers which seems somehow unspecified. I would not change flash shift behavior for 3.1.1 since that would be a potential regression, will have to look at it for 3.2 |
Seems to be a lot better now, but we still have this issue with shift behavior. @Simn could you fix it and write unit tests for this so we make sure we have same behavior everywhere ? |
But |
@Simn I was proposing to use |
Since Haxe has both >> and >>> operators, I think that the signedness of the shift should depend on the operator used, not on the operand type. This puts it in line with other languages with both >> and >>>. |
Looks like cs target is different here as well, UInt >> n results in an unsigned shift (above example returns 1250000000). |
@Herschel ah now that's annoying, because that means we have on some platforms a different behavior on what people are used to... |
Maybe taking the C side is better then. @waneck what about Java, and what's your take on this ? |
The question is always if that's a problem with C# or with gencs. |
On C# there is no unsigned shift operator; rather, >>> casts the left side
to UInt to be able to make it be unsigned.
So yeah, it's how C# deals with shifts. I'm not sure if it even makes sense
to have a signed operator to work on an unsigned type. It would make sense
for me that we simply forbid >> on UInt
|
@waneck given how it's hard to decice on a given behavior, forbidding is maybe the best solution indeed. |
Following the principle of least surprise I'd (also?) vote for making >> an unsigned shift. Forbidding it would only force people to change >> into >>> in some places to make things compile, for no good reason. If there was a good use-case for >> being signed, that'd be different, but I highly doubt that anyone using UInt in the first place would want to perform signed shifts on it, and that an operators behavior depends on the types it's used with makes sense, IMO. |
It was not supposed to be closed, and each new message on this thread makes me change my opinion. I'll sleep on it and look at it again this week with fresh mind |
I'm leaning towards making >> "return" Int. That way it's completely user's choice. |
... but I change my opinion about this on a regular basis as well. |
As long as we don't agree then everything is normal :-) |
We had a discussion on IRC, here's the consensus: The semantics of the shift operators are based on the operand type. >> maintains the sign of the value, >>> zero-shifts. Because UInt values have no sign, >> and >>> are the same for UInt, both zero-shift.
We briefly discussed disallowing >>> for UInt, but decided to have >> and >>> mean the same to avoid people having to change >>> to >> for not much of a reason. What do you think of this, @ncannasse ? |
I seriously thought about it as well, but came to a different conclusion :) But I think that we should also get the same result. For instance
That allows to switch between Int and UInt without changing the actual calculus being done, which is normal for bit operators. This way we specify that I think it actually makes more sense this way if you represent the value as bits, which is what the operator does (operates on bits). |
We have updated our consensus in the sense that nobody cares enough to disagree with your opinion. So let's go ahead with that! |
Nicolas, I think that this is the worst behaviour we can have. On many languages - that don't have the >>> operator -, what determines if it's a signed or unsigned shift is their type (e.g. C, C++, C#...). Porting code from these languages into Haxe would be very cumbersome if we make the behaviour you're suggesting. My own personal view - in light of porting code and the confusion it brings - is that whatever we decide, we should |
We have to be careful:
signed arithmetic right-shift: unsigned both logical and arithmetic right-shift: proposed haxe so called "signed"/arithmetic right-shift on an unsigned integer: Seriously, frankly, please, let's not try to be "clever but different" here.
See especially D which, syntax-wise, also uses >>> for a 0-filling right-shift on signed integers, does allow >> on unsigned types and it does exactly the same - a plain logical, 0-filling right-shift, not a 1-filling pseudo-arithmetic one. Haxe is an awesome language for systems programming. An outstanding one, even. And it will become much better at it. Let's get integers, of all things, right. Please. |
Well, I have to say, after what @ousado said, I'm convinced. |
Indeed, can't disagree with that. |
Another day, another opinion... Ok, let's go with |
Hahahah that comment made my day. :)
|
The text was updated successfully, but these errors were encountered: