[Issue 24040] dmd different to ldc and gcc for ldexp(f)
d-bugmail at puremagic.com
d-bugmail at puremagic.com
Sat Jul 8 13:25:54 UTC 2023
https://issues.dlang.org/show_bug.cgi?id=24040
--- Comment #1 from Iain Buclaw <ibuclaw at gdcproject.org> ---
Arguably introduced by https://github.com/dlang/dmd/pull/7995
Corresponding druntime PR https://github.com/dlang/druntime/pull/2135
Since druntime/2135, there are now float and double overloads of all core.math
intrinsics.
```
float ldexp(float n, int exp);
double ldexp(double n, int exp);
real ldexp(real n, int exp);
```
DMD however only has proper support for the x87 (real) version only - the float
and double declarations are no more than aliases. So the above declarations
might as well instead be
```
float ldexp(real n, int exp);
double ldexp(real n, int exp);
real ldexp(real n, int exp);
```
This affects the call to core.math.ldexp, as the first parameter is implicitly
cast to real. i.e:
```
core.math.ldexp(cast(real)float(mantissa), -213);
```
As per the spec: https://dlang.org/spec/float.html#fp_intermediate_values
> For floating-point operations and expression intermediate values, a greater
> precision can be used than the type of the expression. Only the minimum
> precision is set by the types of the operands, not the maximum.
> Implementation Note: On Intel x86 machines, for example, it is expected
> (but not required) that the intermediate calculations be done to the full
> 80 bits of precision implemented by the hardware.
So the compiler is behaving as expected when it discards the float
cast/construction, as `real` has the greater precision over `float(mantissa)`.
If you want to force a maximum precision (float) then you need to use
core.math.toPrec instead.
```
float a = core.math.toPrec!float(core.math.ldexp(real(mantissa), -213));
```
If dmd still doesn't do the right thing, then that is a problem that needs
addressing. The compiler has to perform a store and load every time a variable
or intermediary value is passed to toPrec.
---
But that's skirting around the sides somewhat. Really, there's lots of
intrinsic declarations in core.math to which dmd does not implement - and
there's no expectation that it should implement either.
DMD's idea of an intrinsic is that it should result in a single instruction.
`real ldexp()` becomes `fscale`, but there is no equivalent instruction for
double or float on x86. Rather, the closest approximation is:
```
fscale; // ldexp()
... // Add 4-6 more instructions here to pop+push the result using a
... // narrower precision (i.e: it should be equivalent to toPrec!T).
```
Instead of these false intrinsics, they should just be regular inline
functions, and all special recognition of them removed from the DMD code
generator/builtins module.
```
real ldexp(real n, int exp); /* intrinsic */
/// ditto
pragma(inline, true)
float ldexp(float n, int exp)
{
return toPrec!float(ldexp(cast(real)n, exp));
}
/// ditto
double ldexp(float n, int exp)
{
return toPrec!double(ldexp(cast(real)n, exp));
}
```
GDC and LDC can continue to map the float and double overloads to their
respective back-end builtins (i.e: libc call), ignoring the function bodies.
---
This could also have been worked around if you used scalbn() instead. ;-)
--
More information about the Digitalmars-d-bugs
mailing list