Bug of the sqrt() compiled by DMD

kdevel kdevel at vogtner.de
Sun Jun 19 22:19:18 UTC 2022


On Sunday, 19 June 2022 at 20:26:27 UTC, Salih Dincer wrote:
> On Sunday, 19 June 2022 at 10:41:32 UTC, Johan wrote:
>> An IEEE 64-bit double (D's `double`) has 52 bits of mantissa. 
>> It _can_ represent the first number without loss of precision 
>> because of the 4 zero bits at the end (56 - 4 = 52). It can 
>> _not_ represent the second number: instead of 0x20 0000 0F90 
>> 97D9, `n2` will store the value 0x20 0000 0F90 97D0, and you 
>> calculate the sqrt of that.
>
> I think the problem is not with bits.

It is not debatable that 94906267² = 9007199515875289 has no 
exact representation in the double type:

```
import std.stdio;

union Double {
    double d;
    ulong u;
}

void main ()
{
    Double d = Double (94906267);
    d.d *= 94906267;
    for (int i = -4; i < 5; ++i) {
       Double e = d;
       e.d += i;
       writefln!"%4d %19.2f %14x" (i, e.d, e.u);
    }
}
```

prints

```
   -4 9007199515875284.00 4340000007c84bea
   -3 9007199515875284.00 4340000007c84bea
   -2 9007199515875286.00 4340000007c84beb
   -1 9007199515875288.00 4340000007c84bec
    0 9007199515875288.00 4340000007c84bec
    1 9007199515875288.00 4340000007c84bec
    2 9007199515875290.00 4340000007c84bed
    3 9007199515875292.00 4340000007c84bee
    4 9007199515875292.00 4340000007c84bee
```

> Because the size of the number is only twice uint.max.

The mantissa of double has only 53 bits which is less than twice 
the number of bits in an (u)int. 94906267 occupies 27 bits:

```
                                |                          |
            94906267 
0000000000000000000000000000000000000101101010000010011110011011
    9007199515875289 
0000000000100000000000000000000000001111100100001001011111011001
```

> When integer type is changed qua long, the problem is fixed.

I would say that the problem is revealed. IMHO the result should 
not depend on wether the long type is signed or not. dmd seems to 
generate different code for the signed/unsigned cases:

```
import std.math;
ulong r1 (double d)
{
    return cast (ulong) sqrt (d);
}

long r2 (double d)
{
    return cast (long) sqrt (d);
}
```

dmd -O -c / objdump -Cdarw shows what goes on:
```
Disassembly of section .text.ulong sub.r1(double):

0000000000000000 <ulong sub.r1(double)>:
    0:   55                      push   %rbp
    1:   48 8b ec                mov    %rsp,%rbp
    4:   48 83 ec 10             sub    $0x10,%rsp
    8:   f2 0f 11 45 f0          movsd  %xmm0,-0x10(%rbp)
    d:   dd 45 f0                fldl   -0x10(%rbp)
   10:   d9 fa                   fsqrt
   12:   b9 00 00 00 80          mov    $0x80000000,%ecx
   17:   c7 45 f0 00 00 00 00    movl   $0x0,-0x10(%rbp)
   1e:   89 4d f4                mov    %ecx,-0xc(%rbp)
   21:   c7 45 f8 3e 40 bf 0f    movl   $0xfbf403e,-0x8(%rbp)
   28:   db 6d f0                fldt   -0x10(%rbp)
   2b:   d8 d9                   fcomp  %st(1)
   2d:   df e0                   fnstsw %ax
   2f:   d9 7d fc                fnstcw -0x4(%rbp)
   32:   d9 6d fa                fldcw  -0x6(%rbp)
   35:   f6 c4 01                test   $0x1,%ah
   38:   74 18                   je     52 <ulong 
sub.r1(double)+0x52>
   3a:   db 6d f0                fldt   -0x10(%rbp)
   3d:   de e9                   fsubrp %st,%st(1)
   3f:   df 7d f0                fistpll -0x10(%rbp)
   42:   48 8b 45 f0             mov    -0x10(%rbp),%rax
   46:   48 c1 e1 20             shl    $0x20,%rcx
   4a:   48 03 c1                add    %rcx,%rax
   4d:   d9 6d fc                fldcw  -0x4(%rbp)
   50:   eb 0a                   jmp    5c <ulong 
sub.r1(double)+0x5c>
   52:   df 7d f0                fistpll -0x10(%rbp)
   55:   48 8b 45 f0             mov    -0x10(%rbp),%rax
   59:   d9 6d fc                fldcw  -0x4(%rbp)
   5c:   c9                      leaveq
   5d:   c3                      retq
         ...

Disassembly of section .text.long sub.r2(double):

0000000000000000 <long sub.r2(double)>:
    0:   55                      push   %rbp
    1:   48 8b ec                mov    %rsp,%rbp
    4:   48 83 ec 10             sub    $0x10,%rsp
    8:   f2 0f 11 45 f0          movsd  %xmm0,-0x10(%rbp)
    d:   dd 45 f0                fldl   -0x10(%rbp)
   10:   d9 fa                   fsqrt
   12:   dd 5d f0                fstpl  -0x10(%rbp)
   15:   f2 0f 10 45 f0          movsd  -0x10(%rbp),%xmm0
   1a:   f2 48 0f 2c c0          cvttsd2si %xmm0,%rax
   1f:   c9                      leaveq
   20:   c3                      retq
   21:   00 00                   add    %al,(%rax)
         ...

```

The unsigned part


More information about the Digitalmars-d mailing list