Ответ
Тип double в C# — это 64-битное число с плавающей запятой, соответствующее стандарту IEEE 754. В памяти оно занимает 8 байт (64 бита) и состоит из трех частей:
- Знак (Sign) — 1 бит.
0— положительное число.1— отрицательное число.
- Экспонента (Exponent) — 11 бит.
- Хранит степень двойки для нормализованного числа. Используется смещенное представление (bias = 1023). Это означает, что фактическая экспонента вычисляется как
хранимое_значение - 1023.
- Хранит степень двойки для нормализованного числа. Используется смещенное представление (bias = 1023). Это означает, что фактическая экспонента вычисляется как
- Мантисса (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.
Ответ 18+ 🔞
Слушай, а вот этот твой double в C# — это же просто пиздец какой интересный зверь, если разобраться! Ну, смотри, по сути это 64-битная хуйня с плавающей запятой, стандарт IEEE 754, все дела. В памяти занимает 8 байт, то есть 64 бита, и внутри себя она разбита на три куска, как говно в унитазе после хорошей закуски.
Первое — знак. Один бит, проще некуда: нолик — число положительное, единичка — отрицательное. Всё, ебись оно конём.
Дальше — экспонента. Вот тут уже 11 бит, и это, блядь, не просто степень двойки, а так называемое смещённое представление. То есть, чтобы хранить отрицательные степени, они к реальной экспоненте прибавляют 1023 (это bias, смещение). Так что когда ты смотришь на биты, помни: реальная степень = хранимое_значение - 1023. Хитро, да? Ну а как ещё, блядь, отрицательные степени хранить?
И третий кусок — мантисса, она же дробная часть. 52 бита. Но самый прикол в том, что для нормальных чисел там подразумевается ещё одна ведущая единичка, которая не хранится! Это называется скрытый бит. Поэтому фактическая точность — 53 бита, вот тебе и раз, ебать-копать.
Формула, по которой всё это собирается обратно в число, выглядит так:
(-1)^знак * (1 + мантисса) * 2^(экспонента - 1023)
Ну а теперь, чтобы не быть голословным, давай посмотрим на код. Вот как можно разобрать double на части, как гречку на вилке:
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}");
}
}
Запустишь — увидишь, из какой оперы эти -123.456.
А теперь про практику, где собака-то зарыта.
Точность у него примерно 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.
И главный вывод, который надо вытатуировать на жопе: для денег double не годится вообще ни хуя! Там эти ошибки округления тебе всю бухгалтерию порежут, как тупым ножом. Для точных расчётов, особенно финансовых, есть decimal — вот его и юзай, и будет тебе счастье.