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. ;-) --