au icon indicating copy to clipboard operation
au copied to clipboard

Compute magnitudes in the "real part" of a type

Open chiphogg opened this issue 6 months ago • 0 comments

A Magnitude is defined to be a real number. But users can ask for its value in, say, std::complex<int> (either explicitly or implicitly). This is a problem, because in computing that value, we check for overflow, which involves less-than comparisons... but complex numbers are not ordered, so there is no meaningful sense of <!

To fix this, we introduce the notion of RealPart<T>, a type trait that gives a type related to T that does have a notion of comparability. (Since Magnitude is defined to be a real number, calling it "real part" is a good fit.) RealPart<T> is just T in almost all cases, but it becomes "the type of T{}.real()" whenever that exists. This lets us support both std::complex and complex number types from other libraries.

The core of the fix was to make sure all of our Magnitude operations are taking place in RealPart<T> rather than T. This basically boils down to the call to base_power_value, and the implementations for SafeCastingChecker. We also use the real type throughout the :apply_magnitude targets, for two reasons. First, there's an actual bug in clang related to complex-complex division. Second, it should be faster at runtime to only divide by the real part.

This change also has knock-on effects for implicit conversion policy. It turns out the old implementation of CoreImplicitConversionPolicy was always silently assuming that the type was already a real number type. Therefore, we rename it by adding an AssumingReal suffix on the end. The new CoreImplicitConversionPolicy passes RealPart<Rep> along to this, after first checking for a new pitfall to guard against: namely, implicitly converting a complex type to a real type.

Fixes #228.

chiphogg avatar Aug 03 '24 22:08 chiphogg