[OT] The Usual Arithmetic Confusions

Siarhei Siamashka siarhei.siamashka at gmail.com
Fri Feb 4 04:32:23 UTC 2022


On Thursday, 3 February 2022 at 05:50:24 UTC, Walter Bright wrote:
> On 2/2/2022 6:25 PM, Siarhei Siamashka wrote:
>> On Thursday, 3 February 2022 at 01:05:15 UTC, Walter Bright 
>> wrote:
>>> I find it works well. For example,
>>>
>>>     int i;
>>>     byte b = i & 0xFF;
>>>
>>> passes without complaint with VRP.
>> 
>> No, it's doesn't pass: `Error: cannot implicitly convert 
>> expression i & 255 of type int to byte`.
>
> My mistake. b should have been declared as ubyte.

Regarding your original example with the `byte` type. Maybe the 
use of the following code can be encouraged as a good idiomatic 
overflow-safe way to do it in D language?

     int i;
     byte b = i.to!byte;

     i = -129;
     b = i.to!byte; // std.conv.ConvOverflowException

This is 2 characters shorter and IMHO nicer looking than `byte b 
= cast(byte)i;`. An overflow check is done at runtime to catch 
bugs, but good optimizing compilers are actually smart enough to 
eliminate it when the range of possible values of `i` is known at 
compile time. For example:

     void foobar(byte[] a)
     {
         foreach (i ; 0 .. a.length)
             a[i] = (i % 37).to!byte;
     }

Gets compiled into:

$ gdc-12.0.1 -O3 -fno-weak-templates -c test.d && objdump -d 
test.o

     0000000000000000 <_D4test6foobarFAgZv>:
        0:	48 85 ff             	test   %rdi,%rdi
        3:	74 37                	je     3c 
<_D4test6foobarFAgZv+0x3c>
        5:	49 b8 8b 7c d6 0d a6 	movabs $0xdd67c8a60dd67c8b,%r8
        c:	c8 67 dd
        f:	31 c9                	xor    %ecx,%ecx
       11:	0f 1f 80 00 00 00 00 	nopl   0x0(%rax)
       18:	48 89 c8             	mov    %rcx,%rax
       1b:	49 f7 e0             	mul    %r8
       1e:	48 c1 ea 05          	shr    $0x5,%rdx
       22:	48 8d 04 d2          	lea    (%rdx,%rdx,8),%rax
       26:	48 8d 14 82          	lea    (%rdx,%rax,4),%rdx
       2a:	48 89 c8             	mov    %rcx,%rax
       2d:	48 29 d0             	sub    %rdx,%rax
       30:	88 04 0e             	mov    %al,(%rsi,%rcx,1)
       33:	48 83 c1 01          	add    $0x1,%rcx
       37:	48 39 cf             	cmp    %rcx,%rdi
       3a:	75 dc                	jne    18 
<_D4test6foobarFAgZv+0x18>
       3c:	c3                   	retq

Slow division is replaced by multiplication and shifts, 
conditional branches are only done to compare `i` with the array 
length. The `.to!byte` part doesn't have any overhead at all and 
bytes are just directly written to the destination array via `mov 
    %al,(%rsi,%rcx,1)` instruction.


More information about the Digitalmars-d mailing list