/// <summary> /// Returns Floor(Log(2,|<paramref name="x"/>|)). /// Note: this is the exponent field of a double precision number. /// <para>If x is NaN then x is returned</para> /// <para>If x == ±∞ then +∞ is returned</para> /// <para>If x == ±0 then -∞ is returned</para> /// </summary> /// <param name="x">Argument</param> public static double Logb(double x) { IEEEDouble rep = new IEEEDouble(x); // Check for +/- Inf or NaN if (!rep.IsFinite) { if ( rep.IsInfinity ) return double.PositiveInfinity; Policies.ReportDomainError("Logb(x: {0}): NaNs not allowed", x); return x; } if ( x == 0 ) { Policies.ReportPoleError("Logb(x: {0}): Logb(0) == -∞", x); return double.NegativeInfinity; } int exponent = 0; if (rep.IsSubnormal) { // Multiply by 2^53 to normalize the number const double normalMult = (1L << IEEEDouble.MantissaBits); rep = new IEEEDouble(x * normalMult); exponent = -IEEEDouble.MantissaBits; Debug.Assert(!rep.IsSubnormal); } exponent += rep.ExponentValue; return exponent; }
/// <summary> /// Returns <paramref name="x"/> * 2^n /// </summary> /// <param name="x">Argument</param> /// <param name="n">The binary exponent (2^n)</param> /// <returns> /// Returns <paramref name="x"/> * 2^n /// <para>If <paramref name="x"/> is NaN, returns NaN.</para> /// <para>If <paramref name="x"/> is ±0 or ±∞, returns x.</para> /// <para>If n is 0, returns x.</para> /// </returns> public static double Ldexp(double x, int n) { const double toNormal = (1L << IEEEDouble.MantissaBits); const double fromNormal = 1.0/(1L << IEEEDouble.MantissaBits); // handle special cases if (double.IsNaN(x)) { Policies.ReportDomainError("Ldexp(x: {0}, n: {1}): Requires x not NaN", x, n); return x; } if (double.IsInfinity(x)) return x; // +/-Inf if (x == 0 || n == 0) return x; // +/-0, x IEEEDouble bits = new IEEEDouble(x); int xExp = 0; if ( bits.IsSubnormal ) { bits = new IEEEDouble(x * toNormal); xExp -= IEEEDouble.MantissaBits; } xExp += bits.ExponentValue + n; // there will be an overflow if (xExp > IEEEDouble.MaxBinaryExponent) return (x > 0) ? double.PositiveInfinity : double.NegativeInfinity; // cannot represent with a denorm -- underflow if (xExp < IEEEDouble.MinBinaryDenormExponent) return (x > 0) ? 0.0 : -0.0; if (xExp >= IEEEDouble.MinBinaryExponent) { // in the normal range - just set the exponent to the sum bits.ExponentValue = xExp; return bits; } // We have a denormalized number #if true // Use C99 definition x*2^exp xExp += IEEEDouble.MantissaBits; Debug.Assert(xExp >= IEEEDouble.MinBinaryExponent); bits.ExponentValue = xExp; return ((double)bits) * fromNormal; #else // Make it compatible with MS C library - no rounding for denorms // kept for future reference int shift = IEEEDouble.MinBinaryExponent - xExp; Debug.Assert(shift > 0); // Recover the hidden mantissa bit and shift const Int64 impliedbit = 1L << IEEEDouble.MantissaBits; // 1.MANTISSA = 1 << 52 Int64 mantissa = (bits.Significand | impliedbit) >> shift; return BitConverter.Int64BitsToDouble(bits.Sign | mantissa); #endif }
/// <summary> /// Returns the fraction where <paramref name="x" /> == fraction * 2 ^ exponent. /// <para>Note: |fraction| is in [0.5,1.0) or is 0</para> /// <para>if x is NaN or ±∞ then x is returned and the exponent is unspecified</para> /// </summary> /// <param name="x">Argument</param> /// <param name="exponent">Output the exponent value</param> /// <returns>System.Double.</returns> public static double Frexp(double x, out int exponent) { exponent = 0; // Check for +/- Inf or NaN if (double.IsNaN(x)) { Policies.ReportDomainError("Frexp(x: {0},...): NaNs not allowed", x); return x; } if (double.IsInfinity(x) || x == 0 ) return x; // +/-0, +/-Inf IEEEDouble bits = new IEEEDouble(x); if (bits.IsSubnormal) { // Multiply by 2^52 to normalize the number const double normalMult = (1L << IEEEDouble.MantissaBits); bits = new IEEEDouble(x * normalMult); exponent = -IEEEDouble.MantissaBits; Debug.Assert(!bits.IsSubnormal); } // x is normal from here on // must return x in [0.5,1.0) = [2^-1,2^0) // so: x_exponent must = -1 while keeping the mantissa and sign the same exponent += bits.ExponentValue + 1; // 2^0 = 1 = 0.5 * 2^1; so increment exponent // replace the exponent with 2^-1 so that x is in [0.5,1.0) const Int64 ExpHalf = 0x3fe0000000000000; return BitConverter.Int64BitsToDouble( (bits & ~IEEEDouble.ExponentMask)| ExpHalf ); }