Thread overview
ubyte + ubyte = int
Oct 26, 2022
0xEAB
Oct 26, 2022
Ali Çehreli
Oct 26, 2022
Ali Çehreli
Oct 27, 2022
Salih Dincer
Oct 27, 2022
Salih Dincer
October 26, 2022
@safe:

void main()
{
    import std.stdio : writeln;
    writeln(ubyte(4).toHexDigit);
}

ubyte toHexDigit(ubyte decimal) pure nothrow @nogc
{
    if (decimal < 10)
        return (decimal + ubyte('0'));

    if (decimal < 16)
        return (decimal - ubyte(10) + ubyte('A'));

    return '\xFF';
}
onlineapp.d(12): Error: cannot implicitly convert expression `cast(int)decimal + 48` of type `int` to `ubyte`
onlineapp.d(15): Error: cannot implicitly convert expression `cast(int)decimal - 10 + 65` of type `int` to `ubyte`

I guess, this fancy behavior is inherited from C.

I know this is advanced stuff, but the compiler could even prove that the calculation(s) won’t go beyond ubyte.max.

October 26, 2022
On 10/26/22 14:10, 0xEAB wrote:

> I guess, this fancy behavior is inherited from C.

Yes. It is called integer promotions:

  https://dlang.org/spec/type.html#integer-promotions

(The next section is related as well.)

> I know this is advanced stuff, but the compiler *could* even prove that
> the calculation(s) won’t go beyond `ubyte.max`.

Apparently it does not go that far but there is Value Range Propagation:

  https://www.digitalmars.com/articles/b62.html

That article links to this forum post:


https://www.digitalmars.com/d/archives/digitalmars/D/Value_Preservation_and_Polysemy_80224.html#N80293

Ali

October 26, 2022
On 10/26/22 14:19, Ali Çehreli wrote:

>    https://dlang.org/spec/type.html#integer-promotions

Reading "Value Range Propagation" further down that link, I learned that e.g. masking 'decimal' with 0xf makes the code compile:

        return ((decimal & 0xf) + ubyte('0'));

        return ((decimal & 0xf) - ubyte(10) + ubyte('A'));

Ali

October 27, 2022

On Wednesday, 26 October 2022 at 21:10:54 UTC, 0xEAB wrote:

>

I know this is advanced stuff, but the compiler could even prove that the calculation(s) won’t go beyond ubyte.max.

//... => char?
        return (decimal - ubyte(10) + ubyte('A'));

    return '\xFF';
}

You should help the compiler with return type:

char toHexDigit(ubyte decimal) pure nothrow @nogc
{
    if (decimal < 10)
        return cast(char) (decimal + ubyte('0'));

    if (decimal < 16)
        return cast(char) (decimal - ubyte(10) + ubyte('A'));

    return '\xFF';
}
void main()
{
  import std.stdio : writeln;
  foreach(ubyte n; 0..256)
  {
    const c = n.toHexDigit();
    if(n < 16)
    {
      c.writeln(": ", n);
    } else assert(c == char.init);
  }
} /*
0: 0
1: 1
2: 2
3: 3
4: 4
5: 5
6: 6
7: 7
8: 8
9: 9
A: 10
B: 11
C: 12
D: 13
E: 14
F: 15

Process finished.
*/

SDB@79

October 27, 2022

On Thursday, 27 October 2022 at 01:57:15 UTC, Salih Dincer wrote:

>

You should help the compiler with return type:

I love D, enjoys it when I code...:)

I played a little on the code, and the following is possible and beautiful:

// If you type auto instead of ubyte as return type, the return will be char.
ubyte toHexDigit(bool capital = false)(ubyte decimal)
{
  static if(capital) enum start = 'A';
                else enum start = 'a';
  if(decimal < 10)
    return cast(char)(decimal + ubyte('0'));

  if(decimal < 16)
    return cast(char)(decimal - ubyte(10)
                              + ubyte(start));
  return '\xFF';
}

unittest
{
  assert(is(typeof(toHexDigit(9)) == ubyte));

  assert(toHexDigit(9) == 57); // '9'
  assert(toHexDigit(10) == 97); // 'a'
  assert(toHexDigit!true(10) == 65); // 'A'
}

SDB@79