Classificators for double extended floating point numbers

This commit is contained in:
Eugen Wissner 2017-09-17 10:30:12 +02:00
parent 27146f7e0c
commit 586d12b6c7
2 changed files with 121 additions and 90 deletions

View File

@ -12,6 +12,68 @@
*/ */
module tanya.math.fp; module tanya.math.fp;
import tanya.math.nbtheory;
/**
* Floating-point number precisions according to IEEE-754.
*/
enum IEEEPrecision : ubyte
{
/// Single precision: 64-bit.
single = 4,
/// Single precision: 64-bit.
double_ = 8,
/// Extended precision: 80-bit.
extended = 10,
}
/**
* Tests the precision of floating-point type $(D_PARAM F).
*
* For $(D_KEYWORD float), $(D_PSYMBOL ieeePrecision) always evaluates to
* $(D_INLINECODE IEEEPrecision.single); for $(D_KEYWORD double) - to
* $(D_INLINECODE IEEEPrecision.double). It returns different values only
* for $(D_KEYWORD real), since $(D_KEYWORD real) is a platform-dependent type.
*
* If $(D_PARAM F) is a $(D_KEYWORD real) and the target platform isn't
* currently supported, static assertion error will be raised (you can use
* $(D_INLINECODE is(typeof(ieeePrecision!F))) for testing the platform support
* without a compilation error).
*
* Params:
* F = Type to be tested.
*
* Returns: Precision according to IEEE-754.
*
* See_Also: $(D_PSYMBOL IEEEPrecision).
*/
template ieeePrecision(F)
if (isFloatingPoint!F)
{
static if (F.sizeof == float.sizeof)
{
enum IEEEPrecision ieeePrecision = IEEEPrecision.single;
}
else static if (F.sizeof == double.sizeof)
{
enum IEEEPrecision ieeePrecision = IEEEPrecision.double_;
}
else version (X86)
{
enum IEEEPrecision ieeePrecision = IEEEPrecision.extended;
}
else version (X86_64)
{
enum IEEEPrecision ieeePrecision = IEEEPrecision.extended;
}
else
{
static assert(false, "Unsupported IEEE 754 floating point precision");
}
}
private union FloatBits(F) private union FloatBits(F)
{ {
F floating; F floating;
@ -25,7 +87,7 @@ private union FloatBits(F)
} }
else static if (ieeePrecision!F == IEEEPrecision.extended) else static if (ieeePrecision!F == IEEEPrecision.extended)
{ {
struct struct // Little-endian.
{ {
ulong mantissa; ulong mantissa;
ushort exp; ushort exp;
@ -33,7 +95,7 @@ private union FloatBits(F)
} }
else else
{ {
static assert(false, "Unsupported IEEE-754 floating point representation"); static assert(false, "Unsupported IEEE 754 floating point precision");
} }
} }
@ -88,6 +150,25 @@ if (isFloatingPoint!F)
} }
else static if (ieeePrecision!F == IEEEPrecision.extended) else static if (ieeePrecision!F == IEEEPrecision.extended)
{ {
if (bits.exp == 0x7fff)
{
if ((bits.mantissa & 0x7fffffffffffffff) == 0)
{
return FloatingPointClass.infinite;
}
else
{
return FloatingPointClass.nan;
}
}
else if (bits.exp == 0)
{
return FloatingPointClass.subnormal;
}
else if (bits.mantissa < 0x8000000000000000) // "Unnormal".
{
return FloatingPointClass.nan;
}
} }
return FloatingPointClass.normal; return FloatingPointClass.normal;
@ -96,94 +177,87 @@ if (isFloatingPoint!F)
bool isFinite(F)(F x) bool isFinite(F)(F x)
if (isFloatingPoint!F) if (isFloatingPoint!F)
{ {
FloatBits!F bits;
static if (ieeePrecision!F == IEEEPrecision.single) static if (ieeePrecision!F == IEEEPrecision.single)
{ {
FloatBits!F bits;
bits.floating = x; bits.floating = x;
bits.integral &= 0x7f800000; bits.integral &= 0x7f800000;
return bits.integral != 0x7f800000; return bits.integral != 0x7f800000;
} }
else static if (ieeePrecision!F == IEEEPrecision.double_) else static if (ieeePrecision!F == IEEEPrecision.double_)
{ {
FloatBits!F bits;
bits.floating = x; bits.floating = x;
bits.integral &= 0x7ff0000000000000; bits.integral &= 0x7ff0000000000000;
return bits.integral != 0x7ff0000000000000; return bits.integral != 0x7ff0000000000000;
} }
else static if (ieeePrecision!F == IEEEPrecision.extended) else static if (ieeePrecision!F == IEEEPrecision.extended)
{ {
FloatBits!F bits;
bits.floating = abs(x); bits.floating = abs(x);
return exponent != 0x7fff; return (bits.exp != 0x7fff) && (bits.mantissa >= 0x8000000000000000);
} }
} }
bool isNaN(F)(F x) bool isNaN(F)(F x)
if (isFloatingPoint!F) if (isFloatingPoint!F)
{
static if (ieeePrecision!F == IEEEPrecision.single)
{ {
FloatBits!F bits; FloatBits!F bits;
bits.floating = abs(x); bits.floating = abs(x);
static if (ieeePrecision!F == IEEEPrecision.single)
{
return bits.integral > 0x7f800000; return bits.integral > 0x7f800000;
} }
else static if (ieeePrecision!F == IEEEPrecision.double_) else static if (ieeePrecision!F == IEEEPrecision.double_)
{ {
FloatBits!F bits;
bits.floating = abs(x);
return bits.integral > 0x7ff0000000000000; return bits.integral > 0x7ff0000000000000;
} }
else static if (ieeePrecision!F == IEEEPrecision.extended) else static if (ieeePrecision!F == IEEEPrecision.extended)
{ {
FloatBits!F bits; if ((bits.exp == 0x7fff && (bits.mantissa & 0x7fffffffffffffff) != 0)
bits.floating = abs(x); || ((bits.exp != 0) && (bits.mantissa < 0x8000000000000000)))
return bits.exponent == 0x7fff && bits.mantissa != 0; {
return true;
}
return false;
} }
} }
bool isInfinity(F)(F x) bool isInfinity(F)(F x)
if (isFloatingPoint!F) if (isFloatingPoint!F)
{
static if (ieeePrecision!F == IEEEPrecision.single)
{ {
FloatBits!F bits; FloatBits!F bits;
bits.floating = abs(x); bits.floating = abs(x);
static if (ieeePrecision!F == IEEEPrecision.single)
{
return bits.integral == 0x7f800000; return bits.integral == 0x7f800000;
} }
else static if (ieeePrecision!F == IEEEPrecision.double_) else static if (ieeePrecision!F == IEEEPrecision.double_)
{ {
FloatBits!F bits;
bits.floating = abs(x);
return bits.integral == 0x7ff0000000000000; return bits.integral == 0x7ff0000000000000;
} }
else static if (ieeePrecision!F == IEEEPrecision.extended) else static if (ieeePrecision!F == IEEEPrecision.extended)
{ {
FloatBits!F bits; return (bits.exp == 0x7fff)
bits.floating = abs(x); && ((bits.mantissa & 0x7fffffffffffffff) == 0);
return bits.exponent == 0x7fff && bits.mantissa == 0;
} }
} }
bool isSubnormal(F)(F x) bool isSubnormal(F)(F x)
if (isFloatingPoint!F) if (isFloatingPoint!F)
{
static if (ieeePrecision!F == IEEEPrecision.single)
{ {
FloatBits!F bits; FloatBits!F bits;
bits.floating = abs(x); bits.floating = abs(x);
static if (ieeePrecision!F == IEEEPrecision.single)
{
return bits.integral < 0x800000; return bits.integral < 0x800000;
} }
else static if (ieeePrecision!F == IEEEPrecision.double_) else static if (ieeePrecision!F == IEEEPrecision.double_)
{ {
FloatBits!F bits;
bits.floating = abs(x);
return bits.integral < 0x10000000000000; return bits.integral < 0x10000000000000;
} }
else static if (ieeePrecision!F == IEEEPrecision.extended) else static if (ieeePrecision!F == IEEEPrecision.extended)
{ {
FloatBits!F bits; return bits.exp == 0;
bits.floating = abs(x);
return bits.exponent == 0 && bits.mantissa != 0;
} }
} }
@ -205,9 +279,26 @@ if (isFloatingPoint!F)
return bits.integral != 0 && bits.integral != 0x7ff0000000000000; return bits.integral != 0 && bits.integral != 0x7ff0000000000000;
} }
else static if (ieeePrecision!F == IEEEPrecision.extended) else static if (ieeePrecision!F == IEEEPrecision.extended)
{
return classify(x) == FloatingPointClass.normal;
}
}
bool signBit(F)(F x)
if (isFloatingPoint!F)
{ {
FloatBits!F bits; FloatBits!F bits;
bits.floating = abs(x); bits.floating = x;
return bits.exponent != 0 && exponent != 0x7fff; static if (ieeePrecision!F == IEEEPrecision.single)
{
return (bits.integral & (1 << 31)) != 0;
}
else static if (ieeePrecision!F == IEEEPrecision.double_)
{
return (bits.integral & (1 << 63)) != 0;
}
else static if (ieeePrecision!F == IEEEPrecision.extended)
{
return (bits.exp & (1 << 15)) != 0;
} }
} }

View File

@ -17,66 +17,6 @@ module tanya.math.nbtheory;
import tanya.math.mp; import tanya.math.mp;
import tanya.meta.trait; import tanya.meta.trait;
/**
* Floating-point number precisions according to IEEE-754.
*/
enum IEEEPrecision : ubyte
{
/// Single precision: 64-bit.
single = 4,
/// Single precision: 64-bit.
double_ = 8,
/// Extended precision: 80-bit.
extended = 10,
}
/**
* Tests the precision of floating-point type $(D_PARAM F).
*
* For $(D_KEYWORD float), $(D_PSYMBOL ieeePrecision) always evaluates to
* $(D_INLINECODE IEEEPrecision.single); for $(D_KEYWORD double) - to
* $(D_INLINECODE IEEEPrecision.double). It returns different values only
* for $(D_KEYWORD real), since $(D_KEYWORD real) is a platform-dependent type.
*
* If $(D_PARAM F) is a $(D_KEYWORD real) and the target platform isn't
* currently supported, static assertion error will be raised (you can use
* $(D_INLINECODE is(typeof(ieeePrecision!F))) for testing the platform support
* without a compilation error).
*
* Params:
* F = Type to be tested.
*
* Returns: Precision according to IEEE-754.
*
* See_Also: $(D_PSYMBOL IEEEPrecision).
*/
template ieeePrecision(F)
if (isFloatingPoint!F)
{
static if (F.sizeof == float.sizeof)
{
enum IEEEPrecision ieeePrecision = IEEEPrecision.single;
}
else static if (F.sizeof == double.sizeof)
{
enum IEEEPrecision ieeePrecision = IEEEPrecision.double_;
}
else version (X86)
{
enum IEEEPrecision ieeePrecision = IEEEPrecision.extended;
}
else version (X86_64)
{
enum IEEEPrecision ieeePrecision = IEEEPrecision.extended;
}
else
{
static assert(false, "Unsupported IEEE 754 precision");
}
}
/** /**
* Calculates the absolute value of a number. * Calculates the absolute value of a number.
* *