Страницы

Поиск по вопросам

суббота, 30 ноября 2019 г.

Указание размера битового поля структуры

#c_sharp #struct


Для удобства работы с числами типа 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# указать определенный размер битового поля, и, если да, то
как сие сделать?
    


Ответы

Ответ 1



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

Ответ 2



Ещё один вариант, позволяющий обходиться без небезопасного [StructLayout(LayoutKind.Explicit)], такой: struct Float64 { public Float64(double x) { Double = x; } public static implicit operator Float64(double x) { return new Float64(x); } public static implicit operator double(Float64 x) { return x.Double; } public double Double; public ulong ULong { get => (ulong)BitConverter.DoubleToInt64Bits(Double); set => Double = BitConverter.Int64BitsToDouble((long)value); } ... } Остальной код как в ответе @Qwertiy.

Комментариев нет:

Отправить комментарий