Как double хранится в памяти в C#?

«Как double хранится в памяти в C#?» — вопрос из категории C# Core, который задают на 25% собеседований C# Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Тип double в C# — это 64-битное число с плавающей запятой, соответствующее стандарту IEEE 754. В памяти оно занимает 8 байт (64 бита) и состоит из трех частей:

  1. Знак (Sign) — 1 бит.
    • 0 — положительное число.
    • 1 — отрицательное число.
  2. Экспонента (Exponent) — 11 бит.
    • Хранит степень двойки для нормализованного числа. Используется смещенное представление (bias = 1023). Это означает, что фактическая экспонента вычисляется как хранимое_значение - 1023.
  3. Мантисса (Mantissa/Фракция) — 52 бита.
    • Хранит дробную часть числа. Для нормализованных чисел подразумевается ведущая 1 (скрытый бит), поэтому фактическая точность — 53 бита.

Формула представления: (-1)^знак * (1 + мантисса) * 2^(экспонента - 1023)

Пример в коде:

using System;
using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Explicit)]
struct DoubleToLong
{
    [FieldOffset(0)] public double DoubleValue;
    [FieldOffset(0)] public long LongValue;
}

public class Program
{
    public static void Main()
    {
        double num = -123.456;
        var converter = new DoubleToLong { DoubleValue = num };
        long bits = converter.LongValue;

        // Извлекаем части
        long sign = (bits >> 63) & 0x01;
        long exponent = (bits >> 52) & 0x7FF;
        long mantissa = bits & 0xFFFFFFFFFFFFF;

        Console.WriteLine($"Число: {num}");
        Console.WriteLine($"Знак: {sign}");
        Console.WriteLine($"Экспонента (смещенная): {exponent}");
        Console.WriteLine($"Мантисса (hex): 0x{mantissa:X}");
    }
}

Практические следствия:

  • Точность: ~15-17 десятичных знаков.
  • Ошибки округления: Из-за двоичного представления некоторые десятичные дроби (например, 0.1) не могут быть представлены точно. Это приводит к известной проблеме 0.1 + 0.2 != 0.3.
  • Специальные значения:
    • double.NaN (Not a Number) — результат неопределенных операций (например, 0.0 / 0.0).
    • double.PositiveInfinity и double.NegativeInfinity — результат переполнения (например, 1.0 / 0.0).

Для точных финансовых расчетов используйте decimal.