Округление последних цифр изменений после обновления Windows .NET

После обновления Windows некоторые расчетные значения изменились в последней цифре, например от -0.0776529085243926 до -0.0776529085243925. Изменение всегда уменьшается на единицу, и это касается как четных, так и нечетных чисел. Похоже, это связано с KB4486153, поскольку при возврате этого обновления значения возвращаются к предыдущим.

Это изменение можно увидеть уже при отладке в Visual Studio и наведении курсора на переменную. Позже значение записывается в выходной файл и также изменяется в нем (без запуска отладчика).

Минимально воспроизводимый пример

var output = -0.07765290852439255;
Trace.WriteLine(output); // This printout changes with the update.

var dictionary = new Dictionary<int, double>();
dictionary[0] = output; // Hover over dictionary to see the change in debug mode

Фон

Расчетное значение исходит из

output[date] = input[date] / input[previousDate] - 1;

Не обращая внимания на потерю точности в арифметике с плавающей запятой, я могу произвести вычисления в окне «Немедленное» и получить -0.07765290852439255 как до, так и после обновления.

Однако при наведении указателя мыши на переменную output я вижу
{[2011-01-12 00:00:00, -0.0776529085243926]} перед обновлением и
{[2011-01-12 00:00:00, -0.0776529085243925]} после, и эта разница также распространяется на выходной файл.

Кажется, что вычисленное значение одинаково до и после обновления, но его представление округляется по-разному.

Входные значения:

{[2011-01-11 00:00:00, 0.983561000400506]} 
{[2011-01-12 00:00:00, 0.907184628008246]}

Целевая платформа установлена ​​на .NET Framework 4.6.1

Вопрос

Могу ли я что-то сделать, чтобы сохранить прежнее поведение, сохранив обновления?

Я знаю о потере точности вычислений с плавающей запятой, но почему это изменение происходит после обновления и как мы можем гарантировать, что будущие обновления не изменят представление значений?

KB4486153 - это обновление для Microsoft .NET Framework 4.8, см. https://support.microsoft.com/en-us/help/4486153/microsoft-net-framework-4-8-on-windows-10.-version-1709-windows-10-vers


person Jaroslav K    schedule 21.08.2019    source источник
comment
Для полной ясности: изменяется ли вывод при запуске вашей программы без отладчика? Кроме того, отредактируйте свой вопрос, добавив минимальный воспроизводимый пример.   -  person grooveplex    schedule 21.08.2019
comment
Основываясь только на различных выходных значениях, которые вы указали в своем вопросе, похоже, что это обновление переключилось с округления на усечение для этого типа математической задачи.   -  person Luke    schedule 21.08.2019
comment
Укажите истинную кодировку значения (желательно в шестнадцатеричном формате), а не его значение, преобразованное в строку. Я думаю, вы обнаружите, что расчетное значение не изменилось ни в малейшей степени.   -  person Ben Voigt    schedule 21.08.2019
comment
@grooveplex спасибо, редактировал с уточнениями   -  person Jaroslav K    schedule 21.08.2019
comment
@BenVoigt Вычисленное значение не изменилось, и меня действительно беспокоит строковое представление, поскольку оно в конечном итоге записывается в выходной файл.   -  person Jaroslav K    schedule 21.08.2019
comment
Вы уверены, что это версия для Windows. Есть различия с микропроцессорами (у некоторых есть ошибки), поэтому вы можете увидеть разницу на другом ПК. Также производителями ПК были созданы патчи для исправления ошибок. Поэтому наличие неправильных патчей на ПК может привести к различиям. Также компиляция для отладки и выпуска иногда отличается.   -  person jdweng    schedule 21.08.2019
comment
@jdweng Я подозреваю, что это конкретное обновление, поскольку его возврат также меняет поведение.   -  person Jaroslav K    schedule 21.08.2019
comment
@JaroslavK: Тогда преобразование в строку должно быть частью вашего минимального примера, потому что поведение кода, который у вас сейчас, не меняется, даже немного.   -  person Ben Voigt    schedule 21.08.2019
comment
Я думаю, ваш минимальный пример double v = -0.07765290852439255; Trace.WriteLine(v);, не так ли?   -  person Ben Voigt    schedule 21.08.2019
comment
Или, что еще лучше, инициализируйте значение с помощью BitConverter.Int64BitsToDouble Затем ясно, что разница заключается в преобразовании строки из одного и того же числового представления.   -  person Ben Voigt    schedule 21.08.2019
comment
@BenVoigt Спасибо! Я обновил пример и проверил его, установив обновление еще раз.   -  person Jaroslav K    schedule 21.08.2019
comment
Лучший способ добиться стабильности - правильно округлить двоичное преобразование в десятичное. Любое другое преобразование является произвольным и может быть изменено.   -  person aka.nice    schedule 22.08.2019


Ответы (1)


OP сталкивается с одной из распространенных проблем с математикой с плавающей запятой. Что нужно с новым программным обеспечением: последовательные ответы или лучший ответ? (Апгрейд лучше)


Некоторая информация, которая поможет решить проблему.

var output = -0.07765290852439255;

С общепринятой двоичной64 кодировкой 1 из-за двоичный характер binary64, output принимает точное значение

-0.077652908524392 54987160666132695041596889495849609375

Ниже показаны предыдущие и следующие возможные double в шестнадцатеричном и десятичном формате FP.

                      -0.077652908524392 5
-0x1.3e10f9e8d3217p-4 -0.077652908524392 53599381885351249366067349910736083984375
-0x1.3e10f9e8d3218p-4 -0.077652908524392 54987160666132695041596889495849609375
                      -0.077652908524392 55
-0x1.3e10f9e8d3219p-4 -0.077652908524392 56374939446914140717126429080963134765625
                      -0.077652908524392 6

Лучшее округленное до ближайшего значения -0.077652908524392 55 (которое кодируется точно как -0.077652908524392 5498...) с точностью до одной цифры меньше -0.077652908524392 5. После обновления код выводит лучший ответ - по крайней мере, в этом единственном случае.

Я не считаю это изменением округления, а скорее улучшенным преобразованием в текст.

Могу ли я что-то сделать, чтобы сохранить прежнее поведение, сохранив обновления?

Возможно, но похоже, что обновление дает лучший результат.

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

Использование шестнадцатеричного вывода с плавающей запятой (как с "%a" в C) - это один из способов получить не изменять представление, но недесятичный вывод незнаком.


1 В других кодировках точное значение могло быть ближе к -0.077652908524392 6, чем к -0.077652908524392 5.

person chux - Reinstate Monica    schedule 22.08.2019
comment
IMO, эти отладчики должны печатать минимальное количество десятичных знаков, которые будут округляться до одного и того же числа с плавающей запятой, как и любой приличный REPL в настоящее время. Каждые два разных числа с плавающей запятой должны иметь разную печатную подпись, иначе вывод не будет полностью полезным ... - person aka.nice; 22.08.2019
comment
@ aka.nice Согласитесь с достаточным количеством десятичных цифр. Может оказаться полезным спецификатор ширины Printf для поддержания точности значения с плавающей запятой. - person chux - Reinstate Monica; 22.08.2019
comment
Спасибо! Решение для меня - принять новое поведение. - person Jaroslav K; 23.08.2019