www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Negating a short?

reply Andy Valencia <dont spam.me> writes:
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
next sibling parent Lance Bachmeier <no spam.net> writes:
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 -val
My reading of the spec is that this should be legal: https://dlang.org/spec/type.html#integer-promotions
Nov 05
prev sibling next sibling parent reply Dennis <dkorpel gmail.com> writes:
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
parent reply Andy Valencia <dont spam.me> writes:
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 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.
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?
Nov 05
parent reply Dennis <dkorpel gmail.com> writes:
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
next sibling parent Andy Valencia <dont spam.me> writes:
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
prev sibling parent reply Matheus <matheus gmail.com> writes:
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
parent reply Dennis <dkorpel gmail.com> writes:
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
parent Matheus <matheus gmail.com> writes:
On Wednesday, 6 November 2024 at 16:27:22 UTC, Dennis wrote:
 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.
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.
Nov 06
prev sibling parent reply Salih Dincer <salihdb hotmail.com> writes:
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
parent reply Salih Dincer <salihdb hotmail.com> writes:
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
parent reply Salih Dincer <salihdb hotmail.com> writes:
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
parent Matheus <matheus gmail.com> writes:
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:
 In response to Andy and Matheus, I think implementing your own 
 type might be a solution:
...
Thanks for the info and the code. Matheus.
Nov 06