.NET 11 умеет преобразовывать double в hex и обратно бит в бит
.NET 11 Preview 4 учит double, float и Half форматироваться спецификатором X и разбираться через NumberStyles.HexFloat, выдавая тот же hex-текст IEEE-754, что и printf("%a") в C.
В заметках о библиотеках .NET 11 Preview 4 появилась возможность, которая кажется узкоспециальной до того дня, когда она вам понадобится: double, float и Half теперь можно записывать и считывать обратно в шестнадцатеричной форме IEEE-754. Это та же нотация, которую C и C++ выводят через printf("%a", ...), и она напрямую показывает в тексте каждый бит значения.
Раньше спецификатор X выбрасывал исключение на double
До сих пор строка формата "X" была только для целых чисел. Вызовите её на значении с плавающей точкой, и вы получали FormatException:
double value = Math.PI;
string hex = value.ToString("X"); // .NET 10: throws FormatException
Начиная с .NET 11, тот же вызов возвращает hex-представление float:
using System.Globalization;
double value = Math.PI;
string hex = value.ToString("X"); // "0X1.921FB54442D18P+1"
double round = double.Parse(hex, NumberStyles.HexFloat);
Console.WriteLine(round == value); // True, exact round-trip
Новый флаг NumberStyles.HexFloat указывает Parse и TryParse считывать эту форму обратно. Ведущая часть 0X1. — это мантисса, а P+1 — двоичный порядок, так что вы видите буквальное расположение битов, а не его десятичное приближение.
Десятичное преобразование туда-обратно уже работало, так зачем это нужно
Здесь стоит быть точным. Начиная с .NET Core 3.0 кратчайший форматировщик с гарантией обратимости обеспечивает, что double.Parse(value.ToString()) возвращает ровно тот же double. Так что hex не исправляет ошибку корректности в десятичном преобразовании туда-обратно.
Hex даёт прозрачность и взаимодействие. Десятичный текст double меняет форму в зависимости от значения и не показывает, какие биты установлены. Hex-форма каноническая: каждый различный double отображается в единственную hex-строку, и эта строка сравнима побайтно между платформами и языками. Если вы фиксируете ожидаемые значения в тесте с эталонным файлом, расхождение в один символ в последнем десятичном разряде — настоящая головная боль. Hex делает сравнение точным и очевидным.
Где это приносит пользу
Самое явное преимущество — взаимодействие между языками. Нативная библиотека, которая логирует float через %a, или набор числовых тестов, общий с кодовой базой на C++, выдаёт hex, который .NET теперь может считывать напрямую:
// From a C++ component that printed with %a
string fromNative = "0X1.5BF0A8B145769P+1"; // ~2.718281828...
double e = double.Parse(fromNative, NumberStyles.HexFloat);
Half и float обрабатываются так же, поэтому значения половинной точности, выходящие из ML-конвейера или буфера GPU, проходят преобразование туда-обратно без потерь на промежуточный десятичный текст. Скачайте SDK Preview 4, нацельтесь на net11.0, и в следующий раз, когда понадобится сериализовать float для тестовой фикстуры или отладочного дампа, используйте "X" вместо того, чтобы вручную плясать с BitConverter.DoubleToInt64Bits.
Comments
Sign in with GitHub to comment. Reactions and replies thread back to the comments repo.