fix Issue 5227 - X ^^ FP at compile-time#8071
Conversation
|
Thanks for your pull request, @WalterBright! Bugzilla references
|
6a3b4e0 to
f2ea327
Compare
|
Wouldn't this just add more discrepancies between compile time and runtime? |
|
These are only executed during CTFE, not optimized constant folding. But certainly all the |
|
[I circumvented this problem in LDC by using the D host compiler's |
|
more builtins .... sigh. |
TurkeyMan
left a comment
There was a problem hiding this comment.
I'd love to see a test for the problem case... although I'm sure it works ;)
test/compilable/test5227.d
Outdated
| pragma(msg, "pow()"); | ||
| enum powf = pow(2.5f, -3.0f); pragma(msg, powf); | ||
| enum powd = pow(2.5 , -3.0 ); pragma(msg, powd); | ||
| enum powr = pow(2.5L, -3.0L); pragma(msg, powr); |
There was a problem hiding this comment.
There's no test here for the problem case. Please add a test for fractional exponent, eg: pow(2, 3.5);
That's the case that doesn't work in CTFE. Integer exponent seems to be handled as a different case.
|
OMFG @WalterBright is my homeboy! Yeah, even if you guys reckon this isn't the most sanitary solution, compile errors when you type 2^^1.5 has never been okay. |
Neither does the std.math one, which is why I suggested a review of std.math.
std.math may be better off in druntime. In any case, that's a separate issue. The ctfloat.d implementations are not terrible, they just rely on the C functions which aren't the best. |
|
@TurkeyMan - you'll start complaining again when There are a couple of functions that should not be here in my opinion, I'll pick them out if they haven't been removed since I started writing. |
src/dmd/builtin.d
Outdated
| { | ||
| Expression arg0 = (*arguments)[0]; | ||
| assert(arg0.op == TOK.float64); | ||
| return new RealExp(loc, CTFloat.nearbyint(arg0.toReal()), arg0.type); |
There was a problem hiding this comment.
This makes no sense, because there is no rounding mode at compile time.
There was a problem hiding this comment.
Can 'compile time' specify a static rounding mode? Presumably 'nearest', since it's the hardware default...?
There was a problem hiding this comment.
Having a look at my end, no, gcc emulated floating point does not.
And hardware default for who? SuperH? Motorola 6800? Soft processors on an FPGA? :-)
There was a problem hiding this comment.
You can however convert the float to mpfr and explicitly request a rounding mode.
src/dmd/builtin.d
Outdated
| { | ||
| Expression arg0 = (*arguments)[0]; | ||
| assert(arg0.op == TOK.float64); | ||
| return new RealExp(loc, CTFloat.rint(arg0.toReal()), arg0.type); |
There was a problem hiding this comment.
Likewise having this as a built-in too.
There was a problem hiding this comment.
These two are there for compatibility with LDC, and also as a simple matter of convenience so the user doesn't have to use two paths for the same code.
There was a problem hiding this comment.
Well I'd rather not add builtins that aren't common for all, as it means you will have to use two paths anyway. For example y2lx comes with version(INLINE_Y2LX).
There was a problem hiding this comment.
What's the issue with adding them to gdc?
There was a problem hiding this comment.
These are non existent builtins in gcc.
More generally though adding them as builtins would make them more susceptible to wrong code in the const folder/optimizer given.
setRoundingMode(down);
immutable x = rint(1.8);
setRoundingMode(up);
immutable y = rint(1.8);
|
@ibuclaw And I'll be happy to complain when that comes up! Current situation is much worse that that hypothetical (albeit probable) moment. |
|
Incidentally; I'm used to building with fast-math and the like. I'm accustomed to CTFE not precisely matching runtime results ;) |
Yes different behavior between CTFE and runtime is horrible to debug, because once you add |
There are always going to be slight differences between compile time and run time. Java early on tried to define away the difference, and they were forced to relent. If your algorithm cares about ULP, you really really need to pay close attention to how your expressions are constructed and compiled. It isn't integer math and cannot be treated like integer math. Anyhow, again, issues with accuracy are off topic for this PR. |
|
This should get a changlog entry. |
|
|
||
| add_builtin("_D3std4math3logFNaNbNiNfeZe", &eval_log); | ||
| add_builtin("_D3std4math3logFNaNbNiNfeZe", &eval_log); | ||
| add_builtin("_D3std4math3logFNaNbNiNfeZe", &eval_log); |
There was a problem hiding this comment.
You are adding the same function 3 times here. If the float and double overloads don't exist one builtin should be enough. Same for a number of other functions.
test/compilable/test5227.d
Outdated
| import std.math; | ||
|
|
||
| pragma(msg, "log()"); | ||
| enum logf = log(5.5f); pragma(msg, logf); |
There was a problem hiding this comment.
How come pragma(msg) is used to test this instead of static assert?
There was a problem hiding this comment.
Right. The current tests also test that CTFE log(float|double) etc. return a real, i.e., that there are no float/double overloads, which I'm not sure is desirable.
There was a problem hiding this comment.
The std.math.log function does not have overloads, and passing float/double to it will return a real. I don't think CTFE should do something different than the corresponding std.math function.
There was a problem hiding this comment.
I don't think CTFE should do something different than the corresponding std.math function.
That isn't the question, it's whether the float/double tests should be there if there's no corresponding function. As soon as Phobos gets proper overloads, these DMD tests will fail, so DMD has to be patched in tandem. I'm in favor of adding corresponding CTFE tests after extending Phobos.
A similar thing is blocking dlang/phobos#6272; I added those CTFE tests myself a while ago. ;)
There was a problem hiding this comment.
Put the functions in Phobos first, then in dmd. Not the other way around, because they cannot be tested if put in dmd first.
There was a problem hiding this comment.
I'd also recommend skipping these tests for now: Testing them here to return real will block adding the overloads to phobos.
There was a problem hiding this comment.
Put the functions in Phobos first, then in dmd. Not the other way around, because they cannot be tested if put in dmd first.
Absolutely, but this patch here puts them in dmd first, that's what I've been trying to tell the whole time.
| add_builtin("_D4core4math4fabsFNaNbNiNfeZe", &eval_fabs); | ||
| add_builtin("_D4core4math5expm1FNaNbNiNfeZe", &eval_unimp); | ||
| add_builtin("_D4core4math4exp21FNaNbNiNfeZe", &eval_unimp); | ||
| add_builtin("_D4core4math4exp2FNaNbNiNfeZe", &eval_unimp); |
There was a problem hiding this comment.
&eval_unimp => &eval_exp2 (likewise for expm1 above)
There was a problem hiding this comment.
There isn't an exp2 function in core.math. We could add to it, but that discussion is beyond the scope of this PR.
There was a problem hiding this comment.
So you noticed the wrong mangle & fixed it instead of just removing that useless entry?
There was a problem hiding this comment.
Yes. I don't know why the eval_unimp entries are there, I didn't write that code. But I felt it was beyond the scope of this PR to address that.
There was a problem hiding this comment.
FWIW, the same goes for the @trusted/@safe overloads, the functions are all @safe IIRC.
src/dmd/builtin.d
Outdated
| add_builtin("_D4core4math4fabsFNaNbNiNeeZe", &eval_fabs); | ||
| add_builtin("_D4core4math5expm1FNaNbNiNeeZe", &eval_unimp); | ||
| add_builtin("_D4core4math4exp21FNaNbNiNeeZe", &eval_unimp); | ||
| add_builtin("_D4core4math4exp2FNaNbNiNeeZe", &eval_unimp); |
There was a problem hiding this comment.
BTW: this replaces @safe with @trusted from the overload above. I think this overload can be safely deleted.
src/dmd/builtin.d
Outdated
| add_builtin("_D3std4math4fabsFNaNbNiNfeZe", &eval_fabs); | ||
| add_builtin("_D3std4math5expm1FNaNbNiNfeZe", &eval_unimp); | ||
| add_builtin("_D3std4math4exp21FNaNbNiNfeZe", &eval_unimp); | ||
| add_builtin("_D3std4math4exp2FNaNbNiNfeZe", &eval_unimp); |
There was a problem hiding this comment.
std.math has annotated exp2 with @trusted, so this overload can go in favor of the one a few lines below.
|
Tests are all green. ping @ibuclaw |
|
@ibuclaw please respond |
|
I agree with Ian that Please also avoid testing the return type of fallbacks for non-existing functions, these block adding them to core/std.math. You could replace them with static asserts checking the value, but not the type. |
|
@rainers ok, done. Except I didn't do the static assert, it seemed pointless. |
src/dmd/builtin.d
Outdated
| assert(arg0.op == TOK.float64); | ||
| Expression arg1 = (*arguments)[1]; | ||
| assert(arg1.op == TOK.float64); | ||
| return new RealExp(loc, CTFloat.copysign(arg0.toReal(), cast(int) arg1.toInteger()), arg0.type); |
There was a problem hiding this comment.
LDC fails to comple this line (no implicit conversion to real_t). Why do you cast to int here? copysign expects a real parameter.
There was a problem hiding this comment.
@rainers - Doesn't ldc implement their own builtins module independent of dmd?
There was a problem hiding this comment.
I think so, but as noted, this is a usage of struct longdouble introduced by compilation with LDC. AFAICT passing int here is wrong anyway, should just be arg1.toReal().
|
Apart from the failure LGTM |
|
I guess the build failure is for platforms where dmd uses |
|
@rainers I'm shooting in the dark here with appveyor. Now I'm getting the error: What do you advise I do to make this work? |
|
It needs a small part of #8169: diff --git a/src/dmd/root/longdouble.d b/src/dmd/root/longdouble.d
index 4b3cedc..0ce7acd 100644
--- a/src/dmd/root/longdouble.d
+++ b/src/dmd/root/longdouble.d
@@ -45,6 +45,13 @@ nothrow:
else
ld_set(&this, d);
}
+ this(real r)
+ {
+ static if (real.sizeof > 8)
+ *cast(real*)&this = r;
+ else
+ this(cast(double)r);
+ }
void opAssign(float f) { ld_set(&this, f); }
void opAssign(double f) { ld_set(&this, f); }
@@ -71,6 +78,11 @@ nothrow:
else static if(is(T == double)) return cast(T)ld_read(&this);
else static if(is(T == long)) return ld_readll(&this);
else static if(is(T == ulong)) return ld_readull(&this);
+ else static if(is(T == real))
+ {
+ static if (real.sizeof > 8) return *cast(real*)&this;
+ else return cast(T)ld_read(&this);
+ }
else static assert(false, "usupported type");
}Then this works: and could even be used for the part |
abf9651 to
f1f46b5
Compare
|
@rainers sigh, now I get this error with appveyor: |
|
Sorry, atm I have no idea why the object files start with 0x5ad5. Locally the files have magic 0x14c (the message is still for x64 only) and building works fine. Maybe just another spurious failure? You can restart the build by amending the same commit again. |
|
|
Thanks for fixing this @WalterBright, missing log/exp support in CTFE has been a long-standing annoyance, in particular for precomputing scientific tables. |
|
Thanks @WalterBright! This is the most exciting patch in years! :P |
|
My pleasure! |
Adds the CTFE functions: