Thread overview
[Issue 23856] The problem of accuracy loss in double division
Apr 24, 2023
FeepingCreature
Oct 14, 2023
Basile-z
Oct 14, 2023
Basile-z
Oct 14, 2023
Basile-z
Oct 14, 2023
Basile-z
April 24, 2023
https://issues.dlang.org/show_bug.cgi?id=23856

FeepingCreature <default_357-line@yahoo.de> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |default_357-line@yahoo.de

--- Comment #1 from FeepingCreature <default_357-line@yahoo.de> ---
Are you using the 32-bit version of dmd? The same thing happens with gcc if you -m32. I can't reproduce with the 64-bit version.

--
April 24, 2023
https://issues.dlang.org/show_bug.cgi?id=23856

--- Comment #2 from mzfhhhh@foxmail.com ---
yes, i test again,

dmd -m32 err.d

output:
err.d:15:main 19999
err.d:18:main 20000
err.d:21:main 20000

dmd -m64 err.d

output:
err.d:15:main 20000
err.d:18:main 20000
err.d:21:main 20000

--
April 24, 2023
https://issues.dlang.org/show_bug.cgi?id=23856

--- Comment #3 from mzfhhhh@foxmail.com ---
Test again, there are issues with both 32-bit and 64-bit

import std;

static void conv_err(double r)
{
    ulong tmp = cast(ulong) ((cast(double) 50) / r);

    //dmd -m32 err.d , tmp = 19999
    //dmd -m64 err.d , tmp = 19999
    //ldc2 err err.d , tmp = 20000
    info(tmp);
}

static void conv_ok(double r)
{
    double v = ((cast(double) 50) / r);
    ulong tmp = cast(ulong) v;

    //dmd -m32 err.d , tmp = 20000
    //dmd -m64 err.d , tmp = 20000
    //ldc2 err err.d , tmp = 20000
    info(tmp);
}

void main()
{
    double r = 0.0025;
    conv_err(r);
    conv_ok(r);
}

--
April 24, 2023
https://issues.dlang.org/show_bug.cgi?id=23856

--- Comment #4 from mzfhhhh@foxmail.com ---
(In reply to FeepingCreature from comment #1)
> Are you using the 32-bit version of dmd? The same thing happens with gcc if you -m32. I can't reproduce with the 64-bit version.

Test again, there are issues with both 32-bit and 64-bit

import std;

static void conv_err(double r)
{
    ulong tmp = cast(ulong) ((cast(double) 50) / r);

    //dmd -m32 err.d , tmp = 19999
    //dmd -m64 err.d , tmp = 19999
    //ldc2 err err.d , tmp = 20000
    info(tmp);
}

static void conv_ok(double r)
{
    double v = ((cast(double) 50) / r);
    ulong tmp = cast(ulong) v;

    //dmd -m32 err.d , tmp = 20000
    //dmd -m64 err.d , tmp = 20000
    //ldc2 err err.d , tmp = 20000
    info(tmp);
}

void main()
{
    double r = 0.0025;
    conv_err(r);
    conv_ok(r);
}

--
October 14, 2023
https://issues.dlang.org/show_bug.cgi?id=23856

Basile-z <b2.temp@gmx.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |b2.temp@gmx.com

--- Comment #5 from Basile-z <b2.temp@gmx.com> ---
I suspect that this is not a bug and, even better, that `1999` would actually be the most accurate result.

1. `0.0025` is not exactly representable[0] as a double, it's actually is
*slightly* bigger.
2. The division
   - DMD code will always use the FPU, so SDIV and have a 80 bit internal
precision
   - LDC code will use a SSE instruction, with a 64 bit precision
3. the trunc
   - DMD
     - in conv_err FISTP is used on the internal 80 bit value
     - in conv_ok the 80 bit value goes back to a 64 bits local
       then is reloaded in the FPY, with 16 bit of precision loss,
       and finally the trunc happens (still FISTP)
   - LDC behaves the same in both conv_err and conv_ok because
     the 80 bit intermediate value has never existed.

See disasm here[1].

Note: in the assembly you can see that the rounding mode is set/saved/restored at several places so maybe i'm completely wrong and that would be a DMD backend bug related to that.


[0]: https://www.binaryconvert.com/result_double.html?decimal=048046048048050053 [1]: https://godbolt.org/z/3969TsPG1

--
October 14, 2023
https://issues.dlang.org/show_bug.cgi?id=23856

--- Comment #6 from Basile-z <b2.temp@gmx.com> ---
> `1999` would actually be the most accurate result

Sorry for this non-sense; I meant "considering the real value of 0.0025", i.e a bit greater.

--
October 14, 2023
https://issues.dlang.org/show_bug.cgi?id=23856

Basile-z <b2.temp@gmx.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
           Severity|critical                    |normal

--
October 14, 2023
https://issues.dlang.org/show_bug.cgi?id=23856

Basile-z <b2.temp@gmx.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
           Keywords|                            |backend

--