| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537 |
- using System;
- namespace ChocolArm64.Instruction
- {
- static class ASoftFloat
- {
- static ASoftFloat()
- {
- InvSqrtEstimateTable = BuildInvSqrtEstimateTable();
- RecipEstimateTable = BuildRecipEstimateTable();
- }
- private static readonly byte[] RecipEstimateTable;
- private static readonly byte[] InvSqrtEstimateTable;
- private static byte[] BuildInvSqrtEstimateTable()
- {
- byte[] Table = new byte[512];
- for (ulong index = 128; index < 512; index++)
- {
- ulong a = index;
- if (a < 256)
- {
- a = (a << 1) + 1;
- }
- else
- {
- a = (a | 1) << 1;
- }
- ulong b = 256;
- while (a * (b + 1) * (b + 1) < (1ul << 28))
- {
- b++;
- }
- b = (b + 1) >> 1;
- Table[index] = (byte)(b & 0xFF);
- }
- return Table;
- }
- private static byte[] BuildRecipEstimateTable()
- {
- byte[] Table = new byte[256];
- for (ulong index = 0; index < 256; index++)
- {
- ulong a = index | 0x100;
- a = (a << 1) + 1;
- ulong b = 0x80000 / a;
- b = (b + 1) >> 1;
- Table[index] = (byte)(b & 0xFF);
- }
- return Table;
- }
- public static float InvSqrtEstimate(float x)
- {
- return (float)InvSqrtEstimate((double)x);
- }
- public static double InvSqrtEstimate(double x)
- {
- ulong x_bits = (ulong)BitConverter.DoubleToInt64Bits(x);
- ulong x_sign = x_bits & 0x8000000000000000;
- long x_exp = (long)((x_bits >> 52) & 0x7FF);
- ulong scaled = x_bits & ((1ul << 52) - 1);
- if (x_exp == 0x7FF && scaled != 0)
- {
- // NaN
- return BitConverter.Int64BitsToDouble((long)(x_bits | 0x0008000000000000));
- }
- if (x_exp == 0)
- {
- if (scaled == 0)
- {
- // Zero -> Infinity
- return BitConverter.Int64BitsToDouble((long)(x_sign | 0x7FF0000000000000));
- }
- // Denormal
- while ((scaled & (1 << 51)) == 0)
- {
- scaled <<= 1;
- x_exp--;
- }
- scaled <<= 1;
- }
- if (x_sign != 0)
- {
- // Negative -> NaN
- return BitConverter.Int64BitsToDouble((long)0x7FF8000000000000);
- }
- if (x_exp == 0x7ff && scaled == 0)
- {
- // Infinity -> Zero
- return BitConverter.Int64BitsToDouble((long)x_sign);
- }
- if (((ulong)x_exp & 1) == 1)
- {
- scaled >>= 45;
- scaled &= 0xFF;
- scaled |= 0x80;
- }
- else
- {
- scaled >>= 44;
- scaled &= 0xFF;
- scaled |= 0x100;
- }
- ulong result_exp = ((ulong)(3068 - x_exp) / 2) & 0x7FF;
- ulong estimate = (ulong)InvSqrtEstimateTable[scaled];
- ulong fraction = estimate << 44;
- ulong result = x_sign | (result_exp << 52) | fraction;
- return BitConverter.Int64BitsToDouble((long)result);
- }
- public static float RecipEstimate(float x)
- {
- return (float)RecipEstimate((double)x);
- }
- public static double RecipEstimate(double x)
- {
- ulong x_bits = (ulong)BitConverter.DoubleToInt64Bits(x);
- ulong x_sign = x_bits & 0x8000000000000000;
- ulong x_exp = (x_bits >> 52) & 0x7FF;
- ulong scaled = x_bits & ((1ul << 52) - 1);
- if (x_exp >= 2045)
- {
- if (x_exp == 0x7ff && scaled != 0)
- {
- // NaN
- return BitConverter.Int64BitsToDouble((long)(x_bits | 0x0008000000000000));
- }
- // Infinity, or Out of range -> Zero
- return BitConverter.Int64BitsToDouble((long)x_sign);
- }
- if (x_exp == 0)
- {
- if (scaled == 0)
- {
- // Zero -> Infinity
- return BitConverter.Int64BitsToDouble((long)(x_sign | 0x7FF0000000000000));
- }
- // Denormal
- if ((scaled & (1ul << 51)) == 0)
- {
- x_exp = ~0ul;
- scaled <<= 2;
- }
- else
- {
- scaled <<= 1;
- }
- }
- scaled >>= 44;
- scaled &= 0xFF;
- ulong result_exp = (2045 - x_exp) & 0x7FF;
- ulong estimate = (ulong)RecipEstimateTable[scaled];
- ulong fraction = estimate << 44;
- if (result_exp == 0)
- {
- fraction >>= 1;
- fraction |= 1ul << 51;
- }
- else if (result_exp == 0x7FF)
- {
- result_exp = 0;
- fraction >>= 2;
- fraction |= 1ul << 50;
- }
- ulong result = x_sign | (result_exp << 52) | fraction;
- return BitConverter.Int64BitsToDouble((long)result);
- }
- public static float RecipStep(float op1, float op2)
- {
- return (float)RecipStep((double)op1, (double)op2);
- }
- public static double RecipStep(double op1, double op2)
- {
- op1 = -op1;
- ulong op1_bits = (ulong)BitConverter.DoubleToInt64Bits(op1);
- ulong op2_bits = (ulong)BitConverter.DoubleToInt64Bits(op2);
- ulong op1_sign = op1_bits & 0x8000000000000000;
- ulong op2_sign = op2_bits & 0x8000000000000000;
- ulong op1_other = op1_bits & 0x7FFFFFFFFFFFFFFF;
- ulong op2_other = op2_bits & 0x7FFFFFFFFFFFFFFF;
- bool inf1 = op1_other == 0x7FF0000000000000;
- bool inf2 = op2_other == 0x7FF0000000000000;
- bool zero1 = op1_other == 0;
- bool zero2 = op2_other == 0;
- if ((inf1 && zero2) || (zero1 && inf2))
- {
- return 2.0;
- }
- else if (inf1 || inf2)
- {
- // Infinity
- return BitConverter.Int64BitsToDouble((long)(0x7FF0000000000000 | (op1_sign ^ op2_sign)));
- }
- return 2.0 + op1 * op2;
- }
- public static float ConvertHalfToSingle(ushort x)
- {
- uint x_sign = (uint)(x >> 15) & 0x0001;
- uint x_exp = (uint)(x >> 10) & 0x001F;
- uint x_mantissa = (uint)x & 0x03FF;
- if (x_exp == 0 && x_mantissa == 0)
- {
- // Zero
- return BitConverter.Int32BitsToSingle((int)(x_sign << 31));
- }
- if (x_exp == 0x1F)
- {
- // NaN or Infinity
- return BitConverter.Int32BitsToSingle((int)((x_sign << 31) | 0x7F800000 | (x_mantissa << 13)));
- }
- int exponent = (int)x_exp - 15;
- if (x_exp == 0)
- {
- // Denormal
- x_mantissa <<= 1;
- while ((x_mantissa & 0x0400) == 0)
- {
- x_mantissa <<= 1;
- exponent--;
- }
- x_mantissa &= 0x03FF;
- }
- uint new_exp = (uint)((exponent + 127) & 0xFF) << 23;
- return BitConverter.Int32BitsToSingle((int)((x_sign << 31) | new_exp | (x_mantissa << 13)));
- }
- public static float MaxNum(float op1, float op2)
- {
- uint op1_bits = (uint)BitConverter.SingleToInt32Bits(op1);
- uint op2_bits = (uint)BitConverter.SingleToInt32Bits(op2);
- if (IsQNaN(op1_bits) && !IsQNaN(op2_bits))
- {
- op1 = float.NegativeInfinity;
- }
- else if (!IsQNaN(op1_bits) && IsQNaN(op2_bits))
- {
- op2 = float.NegativeInfinity;
- }
- return Max(op1, op2);
- }
- public static double MaxNum(double op1, double op2)
- {
- ulong op1_bits = (ulong)BitConverter.DoubleToInt64Bits(op1);
- ulong op2_bits = (ulong)BitConverter.DoubleToInt64Bits(op2);
- if (IsQNaN(op1_bits) && !IsQNaN(op2_bits))
- {
- op1 = double.NegativeInfinity;
- }
- else if (!IsQNaN(op1_bits) && IsQNaN(op2_bits))
- {
- op2 = double.NegativeInfinity;
- }
- return Max(op1, op2);
- }
- public static float Max(float op1, float op2)
- {
- // Fast path
- if (op1 > op2)
- {
- return op1;
- }
- if (op1 < op2 || (op1 == op2 && op2 != 0))
- {
- return op2;
- }
- uint op1_bits = (uint)BitConverter.SingleToInt32Bits(op1);
- uint op2_bits = (uint)BitConverter.SingleToInt32Bits(op2);
- // Handle NaN cases
- if (ProcessNaNs(op1_bits, op2_bits, out uint op_bits))
- {
- return BitConverter.Int32BitsToSingle((int)op_bits);
- }
- // Return the most positive zero
- if ((op1_bits & op2_bits) == 0x80000000u)
- {
- return BitConverter.Int32BitsToSingle(int.MinValue);
- }
- return 0;
- }
- public static double Max(double op1, double op2)
- {
- // Fast path
- if (op1 > op2)
- {
- return op1;
- }
- if (op1 < op2 || (op1 == op2 && op2 != 0))
- {
- return op2;
- }
- ulong op1_bits = (ulong)BitConverter.DoubleToInt64Bits(op1);
- ulong op2_bits = (ulong)BitConverter.DoubleToInt64Bits(op2);
- // Handle NaN cases
- if (ProcessNaNs(op1_bits, op2_bits, out ulong op_bits))
- {
- return BitConverter.Int64BitsToDouble((long)op_bits);
- }
- // Return the most positive zero
- if ((op1_bits & op2_bits) == 0x8000000000000000ul)
- {
- return BitConverter.Int64BitsToDouble(long.MinValue);
- }
- return 0;
- }
- public static float MinNum(float op1, float op2)
- {
- uint op1_bits = (uint)BitConverter.SingleToInt32Bits(op1);
- uint op2_bits = (uint)BitConverter.SingleToInt32Bits(op2);
- if (IsQNaN(op1_bits) && !IsQNaN(op2_bits))
- {
- op1 = float.PositiveInfinity;
- }
- else if (!IsQNaN(op1_bits) && IsQNaN(op2_bits))
- {
- op2 = float.PositiveInfinity;
- }
- return Min(op1, op2);
- }
- public static double MinNum(double op1, double op2)
- {
- ulong op1_bits = (ulong)BitConverter.DoubleToInt64Bits(op1);
- ulong op2_bits = (ulong)BitConverter.DoubleToInt64Bits(op2);
- if (IsQNaN(op1_bits) && !IsQNaN(op2_bits))
- {
- op1 = double.PositiveInfinity;
- }
- else if (!IsQNaN(op1_bits) && IsQNaN(op2_bits))
- {
- op2 = double.PositiveInfinity;
- }
- return Min(op1, op2);
- }
- public static float Min(float op1, float op2)
- {
- // Fast path
- if (op1 < op2)
- {
- return op1;
- }
- if (op1 > op2 || (op1 == op2 && op2 != 0))
- {
- return op2;
- }
- uint op1_bits = (uint)BitConverter.SingleToInt32Bits(op1);
- uint op2_bits = (uint)BitConverter.SingleToInt32Bits(op2);
- // Handle NaN cases
- if (ProcessNaNs(op1_bits, op2_bits, out uint op_bits))
- {
- return BitConverter.Int32BitsToSingle((int)op_bits);
- }
- // Return the most negative zero
- if ((op1_bits | op2_bits) == 0x80000000u)
- {
- return BitConverter.Int32BitsToSingle(int.MinValue);
- }
- return 0;
- }
- public static double Min(double op1, double op2)
- {
- // Fast path
- if (op1 < op2)
- {
- return op1;
- }
- if (op1 > op2 || (op1 == op2 && op2 != 0))
- {
- return op2;
- }
- ulong op1_bits = (ulong)BitConverter.DoubleToInt64Bits(op1);
- ulong op2_bits = (ulong)BitConverter.DoubleToInt64Bits(op2);
- // Handle NaN cases
- if (ProcessNaNs(op1_bits, op2_bits, out ulong op_bits))
- {
- return BitConverter.Int64BitsToDouble((long)op_bits);
- }
- // Return the most negative zero
- if ((op1_bits | op2_bits) == 0x8000000000000000ul)
- {
- return BitConverter.Int64BitsToDouble(long.MinValue);
- }
- return 0;
- }
- private static bool ProcessNaNs(uint op1_bits, uint op2_bits, out uint op_bits)
- {
- if (IsSNaN(op1_bits))
- {
- op_bits = op1_bits | (1u << 22); // op1 is SNaN, return QNaN op1
- }
- else if (IsSNaN(op2_bits))
- {
- op_bits = op2_bits | (1u << 22); // op2 is SNaN, return QNaN op2
- }
- else if (IsQNaN(op1_bits))
- {
- op_bits = op1_bits; // op1 is QNaN, return QNaN op1
- }
- else if (IsQNaN(op2_bits))
- {
- op_bits = op2_bits; // op2 is QNaN, return QNaN op2
- }
- else
- {
- op_bits = 0;
- return false;
- }
- return true;
- }
- private static bool ProcessNaNs(ulong op1_bits, ulong op2_bits, out ulong op_bits)
- {
- if (IsSNaN(op1_bits))
- {
- op_bits = op1_bits | (1ul << 51); // op1 is SNaN, return QNaN op1
- }
- else if (IsSNaN(op2_bits))
- {
- op_bits = op2_bits | (1ul << 51); // op2 is SNaN, return QNaN op2
- }
- else if (IsQNaN(op1_bits))
- {
- op_bits = op1_bits; // op1 is QNaN, return QNaN op1
- }
- else if (IsQNaN(op2_bits))
- {
- op_bits = op2_bits; // op2 is QNaN, return QNaN op2
- }
- else
- {
- op_bits = 0;
- return false;
- }
- return true;
- }
- private static bool IsQNaN(uint op_bits)
- {
- return (op_bits & 0x007FFFFF) != 0 &&
- (op_bits & 0x7FC00000) == 0x7FC00000;
- }
- private static bool IsQNaN(ulong op_bits)
- {
- return (op_bits & 0x000FFFFFFFFFFFFF) != 0 &&
- (op_bits & 0x7FF8000000000000) == 0x7FF8000000000000;
- }
- private static bool IsSNaN(uint op_bits)
- {
- return (op_bits & 0x007FFFFF) != 0 &&
- (op_bits & 0x7FC00000) == 0x7F800000;
- }
- private static bool IsSNaN(ulong op_bits)
- {
- return (op_bits & 0x000FFFFFFFFFFFFF) != 0 &&
- (op_bits & 0x7FF8000000000000) == 0x7FF0000000000000;
- }
- }
- }
|