Для удобства работы с числами типа System.Double (float64) мне бы хотелось описать подобную структуру:
// cpp code
union float64
{
double x;
struct
{
unsigned long long mant : 52;
unsigned long long exp : 11;
unsigned long long sign : 1;
};
float64(double val) { x = val; }
};
float64 f = -4.0;
// f.mant == 0
// f.exp == 1025 (- 1023 == 2)
// f.sign == 1
Иными словами, мне нужна подноготная числа с плавающей точкой, описанная стандартом IEC 60559:1989 (IEEE 754), в быстром, так сказать, доступе
На C++ это очень легко выразить, оперируя битовыми полями. Однако как сделать подобное на C# (элегантно), я придумать не смог: использование MarshalAsAttribute.SizeConst валидно лишь для строк и массивов
Так что единственный вариант, который я пока что вижу, - использование fixed-полей, из которых потом и будет грузиться нужное мне число. Но данное решение видится мне немного громоздким...
Можно ли средствами C# указать определенный размер битового поля, и, если да, то как сие сделать?
Ответ
https://ideone.com/wGW2qq
using System;
using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Explicit)]
struct Float64
{
public Float64(double x) { ULong = 0; Double = x; }
public static implicit operator Float64(double x) { return new Float64(x); }
public static implicit operator double(Float64 x) { return x.Double; }
[FieldOffset(0)] public double Double;
[FieldOffset(0)] public ulong ULong;
public ulong Mantissa
{
get { return ULong & 0xFFFFFFFFFFFFF; }
set { ULong = ULong & ~0xFFFFFFFFFFFFFULL | value & 0xFFFFFFFFFFFFFULL; }
}
public uint Exp
{
get { return (uint)((ULong >> 52) & 0x7FF); }
set { ULong = ULong & 0x800FFFFFFFFFFFFFULL | ((ulong)(value & 0x7FF) << 52); }
}
public uint Sign
{
get { return (uint)(ULong >> 63); }
set { ULong = ULong & 0x7FFFFFFFFFFFFFFFULL | ((ulong)value << 63); }
}
}
public class Test
{
private static void Print(Float64 x)
{
Console.WriteLine("{0,7} {1:X16} s={4} m={2:X13} e={3:X3}", x.Double, x.ULong, x.Mantissa, x.Exp, x.Sign);
}
public static void Main()
{
foreach (var d in new double [] { 0,1,-1,2,-2,3,-3,3.25,-3.25 })
Print(d);
Float64 x = 1;
Print(x);
++x.Exp; Print(x); // 2
x.Sign = 1; Print(x); // -2
x.Mantissa += 1ULL << 51; Print(x); // -3
--x.Exp; Print(x); // -1.5
}
}
0 0000000000000000 s=0 m=0000000000000 e=000
1 3FF0000000000000 s=0 m=0000000000000 e=3FF
-1 BFF0000000000000 s=1 m=0000000000000 e=3FF
2 4000000000000000 s=0 m=0000000000000 e=400
-2 C000000000000000 s=1 m=0000000000000 e=400
3 4008000000000000 s=0 m=8000000000000 e=400
-3 C008000000000000 s=1 m=8000000000000 e=400
3.25 400A000000000000 s=0 m=A000000000000 e=400
-3.25 C00A000000000000 s=1 m=A000000000000 e=400
1 3FF0000000000000 s=0 m=0000000000000 e=3FF
2 4000000000000000 s=0 m=0000000000000 e=400
-2 C000000000000000 s=1 m=0000000000000 e=400
-3 C008000000000000 s=1 m=8000000000000 e=400
-1.5 BFF8000000000000 s=1 m=8000000000000 e=3FF
Комментариев нет:
Отправить комментарий