digitalmars.D.learn - Negating a short?
- Andy Valencia (17/17) Nov 05 I have a template which has a bit where it negates a value. This
- Lance Bachmeier (3/7) Nov 05 My reading of the spec is that this should be legal:
- Dennis (7/9) Nov 05 This is a relic from when integer promotion was added to unary
- Andy Valencia (13/23) Nov 05 I still see:
- Dennis (47/50) Nov 05 That's right, it only removes the deprecation that requires a
- Andy Valencia (20/29) Nov 05 Thank you! *= it is, then. I guess the oddity is actually that
- Matheus (10/17) Nov 06 Hi Dennis,
- Salih Dincer (25/42) Nov 06 In response to Andy and Matheus, I think implementing your own
- Salih Dincer (22/24) Nov 06 Even in my own type, single overload was enough. So I have to
- Salih Dincer (29/31) Nov 06 Oh, am I too hasty? There was already an s in the test
- Matheus (3/8) Nov 06 Thanks for the info and the code.
I have a template which has a bit where it negates a value. This works well until it encountered a short, where ldc2 complained: integral promotion not done for -val I ended up with this, but is negating a short really this problematic, or did I miss something? static if (!__traits(isUnsigned, T)) { if (val < 0) { static if (__traits(getPointerBitmap, T)[0] < int.sizeof) { val = cast(T)(-(cast(int)val)); } else { val = -val; } } } As always, thanks much in advance, Andy
Nov 05
On Tuesday, 5 November 2024 at 17:32:00 UTC, Andy Valencia wrote:I have a template which has a bit where it negates a value. This works well until it encountered a short, where ldc2 complained: integral promotion not done for -valMy reading of the spec is that this should be legal: https://dlang.org/spec/type.html#integer-promotions
Nov 05
On Tuesday, 5 November 2024 at 17:32:00 UTC, Andy Valencia wrote:I ended up with this, but is negating a short really this problematic, or did I miss something?This is a relic from when integer promotion was added to unary operators to match the behavior of C compilers. You can add the `-preview=intpromote` flag to the compiler to make the deprecation warnings go away for a simple `-x`, but that flag is enabled by default since dmd version 2.099 / ldc version 1.29, so simply updating your compiler should also fix it.
Nov 05
On Tuesday, 5 November 2024 at 19:37:32 UTC, Dennis wrote:On Tuesday, 5 November 2024 at 17:32:00 UTC, Andy Valencia wrote:I still see: tst15.d(6): Error: cannot implicitly convert expression `-cast(int)s` of type `int` to `short` For: import std.stdio : writeln; void main() { short s = -5; s = -s; writeln(s); } This is 1.40 beta4?I ended up with this, but is negating a short really this problematic, or did I miss something?This is a relic from when integer promotion was added to unary operators to match the behavior of C compilers. You can add the `-preview=intpromote` flag to the compiler to make the deprecation warnings go away for a simple `-x`, but that flag is enabled by default since dmd version 2.099 / ldc version 1.29, so simply updating your compiler should also fix it.
Nov 05
On Tuesday, 5 November 2024 at 20:29:08 UTC, Andy Valencia wrote:I still see: tst15.d(6): Error: cannot implicitly convert expression `-cast(int)s` of type `int` to `short`That's right, it only removes the deprecation that requires a double cast to fix, but you still need an explicit cast to truncate the result of `-s` (which got promoted to `int`) back to a `short`, unless the compiler can prove at compile time it will always fit in 16 bits. This analysis is called Value Range Propagation (VRP) in the specification: https://dlang.org/spec/type.html#vrp For example, this compiles: ```D void f(short s) { short t = -s / 2; // will always fit short u = -s % 1024; // will always fit } ``` In your example, you get an error because: - VRP doesn't do data flow analysis: it only looks at the current expression without looking at variable assignments that came before it. It doesn't see the value of `s` will always be -5 in that case (though in your actual code there's not a hardcoded constant like -5 so it wouldn't even matter). - Then, considering `s` could be anything from -32768 to 32767, there's a chance of integer overflow because -(-32768) = 32768 which overflows to -32768 when cast to a short. To signal that this possible overflow is okay, an explicit cast is needed: ```D s = cast(short) -s; ``` You can also use an assignment operator, which allows overflow without explicit cast: ```D s *= -1; ``` Which integer conversions are implicit and which ones must be explicit is somewhat controversial. In this case, the compiler can be annoyingly strict, but there's also cases where the compiler is surprisingly lenient: like implicitly converting `bool` / `char` to `int`, or implicitly converting `int` to `uint` (where negative ints implicitly overflow, a common source of bugs!). There have been proposals to change the behavior, but there's often disagreement on the pros and cons of implicit/explicit. On top of that, existing D code shouldn't be broken, and remain consistent with C. (D is designed so that expressions that are the same as C behave the same as C) So it's hard to design a solution for this.
Nov 05
On Wednesday, 6 November 2024 at 00:00:48 UTC, Dennis wrote:That's right, it only removes the deprecation that requires a double cast to fix, but you still need an explicit cast to truncate the result of `-s` (which got promoted to `int`) back to a `short`, unless the compiler can prove at compile time it will always fit in 16 bits. This analysis is called Value Range Propagation (VRP) in the specification: You can also use an assignment operator, which allows overflow without explicit cast: s *= -1;Thank you! *= it is, then. I guess the oddity is actually that the negation of a long is a long, an int--int. But short and byte -> int. All of them have a value which won't fit in the type when negated. I don't mind much, as I have no plans to do a lot of math on short. But you may get visited by the 8- and 16-bit Mafia at some point! :-) Andy long l = -5; l = -l; writeln(l); int i = -5; i = -i; writeln(i); short s = -5; s = -s; writeln(s); byte b = -5; b = -b; writeln(b);
Nov 05
On Wednesday, 6 November 2024 at 00:00:48 UTC, Dennis wrote:On Tuesday, 5 November 2024 at 20:29:08 UTC, Andy Valencia ... You can also use an assignment operator, which allows overflow without explicit cast: ```D s *= -1; ``` ...Hi Dennis, Shouldn't these two act the same? void main(){ short i = 1; i *= -1; // Works i = i*-1; // Gives: Error: cannot implicitly convert expression `cast(int)i * -1` of type `int` to `short` } Matheus.
Nov 06
On Wednesday, 6 November 2024 at 11:51:41 UTC, Matheus wrote:Shouldn't these two act the same?That would make sense, but you wouldn't make a special case just for that specific expression, and it's hard to find a good rule that generalizes to all expressions.
Nov 06
On Wednesday, 6 November 2024 at 16:27:22 UTC, Dennis wrote:On Wednesday, 6 November 2024 at 11:51:41 UTC, Matheus wrote:I thought that internally: i *= -1; Would be expanded as: i = i * -1; And from the expanded version it would generate the same Assembly/Machine Code and follow the same rules. Thanks, Matheus.Shouldn't these two act the same?That would make sense, but you wouldn't make a special case just for that specific expression, and it's hard to find a good rule that generalizes to all expressions.
Nov 06
On Tuesday, 5 November 2024 at 17:32:00 UTC, Andy Valencia wrote:integral promotion not done for -val ```d I ended up with this, but is negating a short really this problematic, or did I miss something? static if (!__traits(isUnsigned, T)) { if (val < 0) { static if (__traits(getPointerBitmap, T)[0] < int.sizeof) { val = cast(T)(-(cast(int)val)); } else { val = -val; } } } ```In response to Andy and Matheus, I think implementing your own kind might be a solution: ```d void main() { Short foo = { -21 }; foo = foo * -2; assert(foo.s == 42); } struct Short { short s; auto opBinary(string op: "*")(int rhs) { auto result = s * rhs; return Short(cast(short)result); } void opOpAssign(string op: "*")(int rhs) { s *= rhs; } } ``` SDB 79
Nov 06
On Wednesday, 6 November 2024 at 16:25:40 UTC, Salih Dincer wrote:In response to Andy and Matheus, I think implementing your own type might be a solution:Even in my own type, single overload was enough. So I have to correct my mistake: ```d void main() { Short foo = { -21 }; s *= -1; foo = foo * -2; assert(foo.s == 42); } struct Short { short s; auto opBinary(string op: "*")(int rhs) { auto result = s * rhs; return Short(cast(short)result); } } ``` SDB 79
Nov 06
On Wednesday, 6 November 2024 at 16:38:40 UTC, Salih Dincer wrote:In response to Andy and Matheus, I think implementing your own type might be a solution:Oh, am I too hasty? There was already an s in the test environment, so I thought 2 overloads were unnecessary. I don't have a programmer or anything, my brother :) In fact, it would be nice if there was an option to correct the threads we wrote on our forum. At least for a certain period of time. Here is the latest code that works: ```d void main() { Short foo = { 21 }; foo *= -1; foo = foo * -2; assert(foo.s == 42); } struct Short { short s; auto opBinary(string op: "*")(int rhs) { auto result = s * rhs; return Short(cast(short)result); } void opOpAssign(string op: "*")(int rhs) { s *= rhs; } } SDB 79
Nov 06
On Wednesday, 6 November 2024 at 16:48:54 UTC, Salih Dincer wrote:On Wednesday, 6 November 2024 at 16:38:40 UTC, Salih Dincer wrote:Thanks for the info and the code. Matheus.In response to Andy and Matheus, I think implementing your own type might be a solution:...
Nov 06