Jump to page: 1 2
Thread overview
Negating a short?
1 day ago
Andy Valencia
1 day ago
Dennis
1 day ago
Andy Valencia
1 day ago
Dennis
1 day ago
Andy Valencia
17 hours ago
Matheus
13 hours ago
Dennis
10 hours ago
Matheus
13 hours ago
Salih Dincer
13 hours ago
Salih Dincer
12 hours ago
Salih Dincer
10 hours ago
Matheus
1 day ago

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

1 day ago

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

1 day ago

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.

1 day ago

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?

1 day ago

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:

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:

s = cast(short) -s;

You can also use an assignment operator, which allows overflow without explicit cast:

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.

1 day ago

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);
17 hours ago
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.
13 hours ago

On Tuesday, 5 November 2024 at 17:32:00 UTC, Andy Valencia wrote:

>

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;
            }
        }
    }

In response to Andy and Matheus, I think implementing your own kind might be a solution:

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

13 hours ago

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.

13 hours ago

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:

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

« First   ‹ Prev
1 2