Visual Studio 2017, экспериментальный / необязательный - нет такого файла или каталога [дубликат]

Мой ответ довольно длинный, поэтому я разделил его на три раздела. Поскольку вопрос касается математики с плавающей запятой, я делаю акцент на том, что на самом деле делает машина. Я также сделал это для двойной точности (64 бит), но этот аргумент применим в равной степени к любой арифметике с плавающей запятой.

Преамбула

IEEE 754 номер двойной бинарной с плавающей запятой (binary64) представляет собой номер формы

value = (-1) ^ s * (1.m51m50 ... m2m1m0 ) 2 * 2e-1023

в 64 бит:

  • Первый бит является битом знака : 1, если число отрицательно, 0 в противном случае.
  • Следующие 11 бит являются показателем , который является offset на 1023. Другими словами, после чтение битов экспоненты из числа двойной точности 1023 должно быть вычтено для получения мощности двух.
  • Остальные 52 бита представляют собой значение (или мантисса). В мантиссе «подразумеваемый» 1. всегда пропускается, поскольку самый старший бит любого двоичного значения равен 1.

1 - IEEE 754 допускает концепцию с нулевым значением - +0 и -0 обрабатываются по-разному: 1 / (+0) - положительная бесконечность; 1 / (-0) - отрицательная бесконечность. Для нулевых значений биты мантиссы и экспоненты равны нулю. Примечание: нулевые значения (+0 и -0) явно не классифицируются как denormal2.

2 - Это не относится к денормальным номерам , которые имеют показатель смещения нуля (и подразумевается 0.). Диапазон денормальных чисел двойной точности dmin ≤ | x | ≤ dmax, где dmin (наименьшее представимое ненулевое число) составляет 2-1023 - 51 (≈ 4,94 * 10-324) и dmax (наибольшее денормальное число, для которого мантисса полностью состоит из 1 s) составляет 2-1023 + 1 - 2-1023 - 51 (≈ 2.225 * 10-308).


Превращение числа двойной точности в двоичный

Существует множество онлайн-конвертеров для преобразования двойной точности (например, в binaryconvert.com ), но здесь приведен пример кода C # для получения представления IEEE 754 для числа двойной точности (я разделяю три части с двоеточиями (: ):

public static string BinaryRepresentation(double value)
{
    long valueInLongType = BitConverter.DoubleToInt64Bits(value);
    string bits = Convert.ToString(valueInLongType, 2);
    string leadingZeros = new string('0', 64 - bits.Length);
    string binaryRepresentation = leadingZeros + bits;

    string sign = binaryRepresentation[0].ToString();
    string exponent = binaryRepresentation.Substring(1, 11);
    string mantissa = binaryRepresentation.Substring(12);

    return string.Format("{0}:{1}:{2}", sign, exponent, mantissa);
}

Достижение точки: исходный вопрос

(Перейти к нижней части для версии TL; DR)

Катон Джонстон (вопросник) спросил, почему 0.1 + 0.2! = 0.3.

Написано в двоичном (с двоеточиями, разделяющими три части), представления IEEE 754 значений:

0.1 => 0:01111111011:1001100110011001100110011001100110011001100110011010
0.2 => 0:01111111100:1001100110011001100110011001100110011001100110011010

Обратите внимание, что мантисса состоит из повторяющихся цифр 0011. ключ к тому, почему есть какие-либо ошибки в расчетах - 0,1, 0,2 и 0,3 не могут быть представлены в двоичной форме точно в конечном числе двоичных битов не более 1/9, 1/3 или 1/7 могут быть представлены точно в десятичных разрядах .

Преобразование экспонентов в десятичные, удаление смещения и повторное добавление подразумеваемых 1 (в квадратных скобках), 0,1 и 0,2 :

0.1 = 2^-4 * [1].1001100110011001100110011001100110011001100110011010
0.2 = 2^-3 * [1].1001100110011001100110011001100110011001100110011010

Чтобы добавить два числа, показатель должен быть одинаковым, т. е.

0.1 = 2^-3 *  0.1100110011001100110011001100110011001100110011001101(0)
0.2 = 2^-3 *  1.1001100110011001100110011001100110011001100110011010
sum = 2^-3 * 10.0110011001100110011001100110011001100110011001100111

Поскольку сумма не имеет вид 2n * 1. { bbb} мы увеличиваем показатель на единицу и сдвигаем десятичную ( двоичную ) точку, чтобы получить:

sum = 2^-2 * 1.0011001100110011001100110011001100110011001100110011(1)

В мантиссе сейчас 53 бит (53-й квадрат скобки в строке выше). Режим округления по умолчанию для IEEE 754 равен ' Round to Nearest ' - то есть, если число x падает между двумя значениями a и b выбрано значение, в котором наименьший значащий бит равен нулю.

a = 2^-2 * 1.0011001100110011001100110011001100110011001100110011
x = 2^-2 * 1.0011001100110011001100110011001100110011001100110011(1)
b = 2^-2 * 1.0011001100110011001100110011001100110011001100110100

Обратите внимание, что a и b отличаются только последним битом; ...0011 + 1 = ...0100. В этом случае значение с младшим значащим разрядом равно b , поэтому сумма равна:

sum = 2^-2 * 1.0011001100110011001100110011001100110011001100110100

TL; DR

Запись 0.1 + 0.2 в двоичном представлении IEEE 754 (с двоеточиями, разделяющими три части) и сравнивая его с 0.3, это (я положил отдельные биты в квадратные скобки):

0.1 + 0.2 => 0:01111111101:0011001100110011001100110011001100110011001100110[100]
0.3       => 0:01111111101:0011001100110011001100110011001100110011001100110[011]

Преобразован обратно к десятичной, эти значения:

0.1 + 0.2 => 0.300000000000000044408920985006...
0.3       => 0.299999999999999988897769753748...

Разница в точности равна 2-54, что составляет ~ 5.5511151231258 × 10-17 - незначительно (для многих приложений) по сравнению с исходными значениями.

Сравнение последних нескольких бит числа с плавающей запятой по своей сути опасно, как и любой, кто читает знаменитый «

, что каждый компьютерный ученый должен знать о арифметике с плавающей точкой » (который охватывает все основные части этого ответа).

Большинство калькуляторов используют дополнительные охранные цифры , чтобы обойти эту проблему, так как 0.1 + 0.2 даст 0.3: последние несколько биты округлены.

8
задан einpoklum 29 April 2016 в 09:05
поделиться

2 ответа

std::experimental::optional происходит из библиотеки Boost.Optional , и эта реализация хорошо работает в Visual C ++ 12.0 (хотя она немного отличается от ). Ссылка на один заголовок, основанный на документе предложения N3793 , можно найти здесь здесь .

Последний список поддерживаемых C ++ 11/14 / 1z и функции библиотеки, поставляемые с Visual Studio, можно найти в блоге Visual C ++ Team , в частности этой записи . Набор файлов заголовков реализации стандартной библиотеки (и некоторых расширений) из Microsoft можно просмотреть здесь здесь .

10
ответ дан Piotr Skotnicki 26 August 2018 в 05:11
поделиться

Я обнаружил, что эта реализация компилируется с помощью MSVC2013. Обратите внимание, что только компилируется, все остальное не гарантируется.

https://github.com/mapbox/variant/blob/master/include/mapbox/optional.hpp

0
ответ дан Checo R 26 August 2018 в 05:11
поделиться
Другие вопросы по тегам:

Похожие вопросы: