https://issues.dlang.org/show_bug.cgi?id=24040

--- Comment #1 from Iain Buclaw <ibuc...@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. ;-)

--

Reply via email to