On Saturday, 21 October 2017 at 20:17:12 UTC, NX wrote:
I was working on some sort of math library for use in graphical
computing and I wrote something like this:
const float PiOver2 = (atan(1.0) * 4) / 2;
Interestingly enough, I realized that atan() returns double (in
this case) but wait, it's assigned to a float variable!
Compiler didn't even emit warnings, let alone errors.
I see no reason as to why would this be legal in this century,
especially in D.
So can someone tell me what's the argument against this?
Why type conversions differ between integral and floating types?
Why can't I assign a long to an int but it's fine when
assigning double to float?
I think this is a serious topic and needs clarification.
In the meantime:
---
/**
* Wraps a floating point type that doesn't follow D permissive
float conversion
* rules.
*
* In D, as in C++, implicit conversion from $(D double) to $(D
float) is allowed,
* leading to a possible precision loss. This can't happen when
using this wrapper.
*/
struct CoercionSafeFloat(T)
if (isFloatingPoint!T)
{
private T _value;
alias _value this;
enum antiCoercion =
q{
static assert(V.sizeof <= T.sizeof, "coercion from " ~
V.stringof ~
" to " ~ T.stringof ~ " is not allowed");
};
/// Prevent Coercion from construction.
this(V)(V v) {mixin(antiCoercion); _value = v;}
/// Prevent Coercion from assignation.
void opAssign(V)(V v) {mixin(antiCoercion); _value = v;}
/// Prevent Coercion from operator assignation.
void opOpAssign(string op, V)(V v)
{
mixin(antiCoercion);
mixin("_value " ~ op ~ "= v;");
}
}
///
unittest
{
alias Float = CoercionSafeFloat!float;
alias Double = CoercionSafeFloat!double;
alias Real = CoercionSafeFloat!real;
Float f; Double d; Real r;
import std.math;
static assert(!__traits(compiles, f = (atan(1.0) * 4) / 2));
static assert( __traits(compiles, f = (atan(1.0f) * 4f) /
2f));
static assert(!__traits(compiles, d = (atan(1.0L) * 4L) /
2L));
static assert(__traits(compiles, d = f));
static assert(!__traits(compiles, f = d));
static assert(!__traits(compiles, d = r));
static assert(!__traits(compiles, d += r));
static assert(__traits(compiles, r *= d));
}
---
you can get a safer float type if this is a concern.