Jump to page: 1 2
Thread overview
What the heck am i doing wrong? I'm just trying to create a 8 bit unsigned variable.
May 16
monkyyy
May 21
kdevel
May 21
Dom DiSc
May 18
monkyyy
May 16

/+ SDL3 has a function
bool SDL_SetTextureAlphaMod(SDL_Texture *texture, Uint8 alpha);
which has a Uint8 for one of its parameters. So I try to use a ubyte
+/

void main()
{
ubyte a;
a = a + 5; // onlineapp.d(11): Error: cannot implicitly convert expression cast(int)a + 5 of type int to ubyte

import core.stdc.stdint;
uint8_t b;
b = b + 5; // onlineapp.d(17): Error: cannot implicitly convert expression `cast(int)b +
              5` of type `int` to `ubyte`

cast(uint8_t) b = cast(uint8_t) b + cast(uint8_t) 5;  // onlineapp.d(19): Error: cannot
                                                         implicitly convert expression
                                                         `cast(int)b + 5` of type `int`
                                                         to `ubyte`

}

May 16
On Fri, May 16, 2025 at 07:04:24PM +0000, WhatMeWorry via Digitalmars-d-learn wrote: [...]
> void main()
> {
>     ubyte a;
>     a = a + 5; // onlineapp.d(11): Error: cannot implicitly convert expression `cast(int)a + 5` of type `int` to `ubyte`
[...]

Welcome to your first encounter with why I hate D's narrow integer promotion rules.

Basically, `a + 5` gets promoted to int, and that's why it can't be assigned back to `a`.  (Don't ask me why, that's just the way it is and Walter has refused and probably will continue to refuse to change it.)

Ironically, this is OK:

```
	ubyte a;
	a += 5;
```

But writing it as `a = a + 5;` is not.

You can't just decide to use the `<op>=` form to work around this every time either.  For example, how do you negate a ubyte?  Obviously, you can't do this:

```
	ubyte a;
	a -=;
```

But writing it as `a = -a;` runs into the same error, for the same reason.

Instead, you have to work around it with this baroque periphrasis:

```
	ubyte a;
	a = cast(ubyte) -a;
```

Does it make sense?

Yes, it's just a consequence of integer promotion rules.

Does it make sense?

Intuitively, absolutely not.

What's the solution?  Maybe this might interest you:

	https://forum.dlang.org/post/mailman.282.1631547531.21945.digitalmars-d@puremagic.com


T

-- 
Claiming that your operating system is the best in the world because more people use it is like saying McDonalds makes the best food in the world. -- Carl B. Constantine
May 16
On Friday, 16 May 2025 at 19:19:41 UTC, H. S. Teoh wrote:
> 
> Ironically, this is OK:
> 	a += 5;

I dont think its ironic at all; I think it should be heavily used; syntax sugar has semantics meaning given a lack of infinite time for exploring all turing complete machines.
May 17
On Friday, May 16, 2025 1:19:41 PM Mountain Daylight Time H. S. Teoh via Digitalmars-d-learn wrote:
> On Fri, May 16, 2025 at 07:04:24PM +0000, WhatMeWorry via Digitalmars-d-learn wrote: [...]
> > void main()
> > {
> >     ubyte a;
> >     a = a + 5; // onlineapp.d(11): Error: cannot implicitly convert expression `cast(int)a + 5` of type `int` to `ubyte`
> [...]
>
> Welcome to your first encounter with why I hate D's narrow integer promotion rules.
>
> Basically, `a + 5` gets promoted to int, and that's why it can't be assigned back to `a`.  (Don't ask me why, that's just the way it is and Walter has refused and probably will continue to refuse to change it.)

It's because C (and I'm pretty sure the CPU as well) promotes integer types smaller than 32 bits to 32 bits to do arithmetic on them. So, in both C and D, the result is int. In general, C code is supposed to have the same semantics in D, or it's not supposed to be compile, and Walter is insistent that the behavior of arithmetic follows that (the one exception I'm aware of being that D defines what happens with overflow whereas C does not).

The difference between C and D is then that D doesn't allow narrowing conversions. This prevents certain classes of bugs, but when it's combined with the fact that smaller integer types get promoted to int when doing arithmetic, it can definitely become annoying if you want to do arithmetic with smaller integer types.

In some circumstances, VRP (value range propagation) will make it so that the D compiler is able to determine that the result will fit in the smaller integer type, so it will then do the implicit conversion, but because Walter tends to hate data flow analysis, it mostly just works in pretty basic situations.

> Ironically, this is OK:
>
> ```
>   ubyte a;
>   a += 5;
> ```
>
> But writing it as `a = a + 5;` is not.

This is because with +=, you don't get a result that's int. It's just mutating the ubyte, whereas a + 5 _does_ result in int, and you can't implicitly convert int to ubyte, because it's a narrowing conversion. It is kind of ugly that it works in one case but not the other, but it does make logical sense.

Given that Java and C# disallow narrowing conversions, I expect that they have similar restrictions, but I haven't done much with either of them any time recently, so I'm not sure what they do with smaller integer types and arithmetic. It's possible that they allow the narrowing conversion in some specific cases in order to be more user-friendly, but they may also treat it more or less the same way that D does. C and C++ avoid it because they allow narrowing conversions and thus invisibly truncate the result, which creates its own set of problems - but it does also mean that some code that doesn't truncate the values just works, whereas in languages that don't allow narrowing conversions, you typically have to cast, which of course can be annoying. But that's the price of avoiding invisible truncation with arithmetic. It's just that the fact that smaller integer types get converted to int for arithmetic makes it worse, though if they weren't, you'd be getting integer overflow with them pretty frequently, I expect.

- Jonathan M Davis




May 18
On Sat, May 17, 2025 at 07:09:40AM -0600, Jonathan M Davis via Digitalmars-d-learn wrote: [...]
> Given that Java and C# disallow narrowing conversions, I expect that they have similar restrictions, but I haven't done much with either of them any time recently, so I'm not sure what they do with smaller integer types and arithmetic.
[...]
> It's just that the fact that smaller integer types get converted to int for arithmetic makes it worse, though if they weren't, you'd be getting integer overflow with them pretty frequently, I expect.
[...]

This is why my proposed nopromote package uses the wrapper function .np to overtly mark an expression as being computed in narrow width, therefore there's the risk of wraparound issues.

```
import nopromote;
byte b = 120;
b = b.np + 5;	// .np overtly marks this as narrow int arithmetic
b = b.np * 2;   // you're on your own here when this overflows

// Though you should't be using narrow ints if you didn't know how to
// deal with it properly in the first place.
```


T

-- 
The most powerful one-line C program: #include "/dev/tty" -- IOCCC
May 18
On Sunday, 18 May 2025 at 15:30:39 UTC, H. S. Teoh wrote:
> ```
> import nopromote;
> byte b = 120;
> b = b.np + 5;	// .np overtly marks this as narrow int arithmetic
> b = b.np * 2;   // you're on your own here when this overflows
>
> // Though you should't be using narrow ints if you didn't know how to
> // deal with it properly in the first place.
> ```

```d
    short b=120;
    (b+=5)*=5;
    b.writeln;
```

It will get more and more absurd with ()'s but.... well its there already.

May 18
On 5/16/25 12:19 PM, H. S. Teoh wrote:

> Basically, `a + 5` gets promoted to int, and that's why it can't be
> assigned back to `a`.  (Don't ask me why, that's just the way it is and
> Walter has refused and probably will continue to refuse to change it.)

Overloaded functions must also be considered. I doubt there is any code out there that would start behaving unexpectedly with such a change but the following code might be dispatched to another function:

  foo(a + 5);

Related: Template instantiation...

And what if ubyte and int appeared in the same expression:

  int i;
  a + 5 + i;

If the rules were changed, we would still expect the expression to be int I guess. But then, wasn't 5 an int with today's rules? With the proposed change, we would still be following the same rule; roughly: the type of the largest expression...

Such messy rules of programming languages are not easy to clean up and there are natural reasons why they are messy. I agree with Walter that the best action is to follow what other system programming languages do in this case.

Ali

May 21
On Saturday, 17 May 2025 at 13:09:40 UTC, Jonathan M Davis wrote:
> On Friday, May 16, 2025 1:19:41 PM Mountain Daylight Time H. S. Teoh via Digitalmars-d-learn wrote:
>> Welcome to your first encounter with why I hate D's narrow integer promotion rules.
>
> It's because C (and I'm pretty sure the CPU as well) promotes integer types smaller than 32 bits to 32 bits to do arithmetic on them. So, in both C and D, the result is int. In general, C code is supposed to have the same semantics in D, or it's not supposed to be compile, and Walter is insistent that the behavior of arithmetic follows that (the one exception I'm aware of being that D defines what happens with overflow whereas C does not).

The result of unary operations should be of the same type as the source.
The result of binary operations should be of the common type.

It's ok to actually perform the calculation in the machine wordsize (if that's large enough), but should be converted back to the common type, no matter if that would be a lossy cast.

The number of places where this would result in a difference to C is very small, and most of those are bugs anyway.

If the programmer doesn't want to lose accuracy, he should use bigger types to start with.

May 21

On Friday, 16 May 2025 at 19:04:24 UTC, WhatMeWorry wrote:

>
cast(uint8_t) b = cast(uint8_t) b + cast(uint8_t) 5;  // onlineapp.d(19): Error: cannot
                                                         implicitly convert expression
                                                         `cast(int)b + 5` of type `int`
                                                         to `ubyte`

}

I ran into something related which was discussed here. I finally found the advanced search on the forum (an amazing piece of SW) and here's some more information which may be helpful:

https://forum.dlang.org/post/jvvfmbrvfbwkbwbsyctv@forum.dlang.org

Andy

May 21

On Friday, 16 May 2025 at 19:04:24 UTC, WhatMeWorry wrote:

>

/+ SDL3 has a function
bool SDL_SetTextureAlphaMod(SDL_Texture *texture, Uint8 alpha);
which has a Uint8 for one of its parameters. So I try to use a ubyte
+/

[...]

So what would be best practice here?

Declare an int, manually make sure the value stays between 0 and 255, and then
cast it to the ubyte?

« First   ‹ Prev
1 2