Bug of the sqrt() compiled by DMD

Johan j at j.nl
Sun Jun 19 10:41:32 UTC 2022


On Sunday, 19 June 2022 at 08:36:33 UTC, Salih Dincer wrote:
>
> ```d
> import std.math, std.stdio;
> alias integer = ulong;
>
> void main()
> {
>   integer n = 94_906_260;
>   for( ; n <= 94_906_270; ++n)
>   {
>     integer root;
>     double root2, n2 = n * n;
> ```

For some of the values of `n` that you are using, a `double` 
cannot represent `n*n`.

94,906,260 * 94,906,260 = 9,007,198,187,187,600 = 0x1F FFFF C05E 
6D90

94,906,267 * 94,906,267 = 9,007,199,515,875,289 = 0x20 0000 0F90 
97D9

Both results are 56 bit integers.
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.

LDC creates instructions that do the sqrt in `double` domain, but 
DMD converts the `double` to a `real` (80bit floating point on 
x86) and does the sqrt in the `real` domain. Probably that is why 
it seems to work correctly for LDC and not for DMD 
(representation differences of the sqrt result for double and 
real, plus rounding when going back to `ulong`).

-Johan



More information about the Digitalmars-d mailing list