(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);
}
Comment #5 by b2.temp — 2023-10-14T03:59:23Z
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
Comment #6 by b2.temp — 2023-10-14T10:47:19Z
> `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.
Comment #7 by robert.schadek — 2024-12-13T19:28:25Z