Math.Floor() иногда неправильно округляет

        private void OnGroupDownloadProgressed(object sender, IEnumerable<DownloadItem> items)
        {
            Invoke(new MethodInvoker(() =>
            {
                long chunksSummarySize = items.Select(item => item.ChunkSize).Sum();
                long downloaded = items.Select(item => item.DownloadedSize).Sum();

                if (chunksSummarySize > 0L)
                {
                    double percent = 100.0 / chunksSummarySize * downloaded;
                    string percentFormatted = string.Format("{0:F2}", percent);
                    lblGroupProgress.Text = $"Скачано: {FormatSize(downloaded)} / {FormatSize(chunksSummarySize)} ({percentFormatted}%)";
                }
                else
                {
                    lblGroupProgress.Text = "Подключение...";
                }

                LinkedList<MultipleProgressBarItem> list = new LinkedList<MultipleProgressBarItem>();
                foreach (DownloadItem item in items)
                {
                    double percent = 100.0 / item.ChunkSize * item.DownloadedSize;
                    string percentFormatted = string.Format("{0:F2}", percent);

                    string itemText;
                    switch (item.State)
                    {
                        case DownloadItemState.Connecting:
                            itemText = $"{item.VodChunk.FileName}: Connecting...";
                            break;

                        case DownloadItemState.Downloading:
                        case DownloadItemState.Finished:
                        case DownloadItemState.Errored:
                            itemText = $"{item.VodChunk.FileName}: " +
                                $"{FormatSize(item.DownloadedSize)} / {FormatSize(item.ChunkSize)} ({percentFormatted}%)";
                            break;

                        default:
                            itemText = null;
                            break;
                    }

                    int percentRounded = (int)Math.Floor(percent);

                    if (item.DownloadedSize == item.ChunkSize && percentRounded == 99)
                    {
                        string t = $"{item.DownloadedSize} / {item.ChunkSize} | {percent} | {percentRounded}\n{itemText}";
                        System.Diagnostics.Debug.WriteLine(t);
                   }

                    MultipleProgressBarItem mpi = new MultipleProgressBarItem(
                        0, 100, percentRounded, itemText, Color.Lime);
                    list.AddLast(mpi);
                }
                multipleProgressBarGroup.SetItems(list);
            }));
        }

Иногда срабатывает вот это условие:

                    if (item.DownloadedSize == item.ChunkSize && percentRounded == 99)
                    {
                        string t = $"{item.DownloadedSize} / {item.ChunkSize} | {percent} | {percentRounded}\n{itemText}";
                        System.Diagnostics.Debug.WriteLine(t);
                   }

и в консоль выводится это:
Снимок экрана 2024-01-25 171949
Не пойму почему :thinking: Там же ровно 100, а не 99,999999999999.
Я знаю, что операции над числами с плавающей запятой не идеально точные. Но если они прям настолько не точные, на кой тогда хранить все эти 16 (или сколько их там) знаков после запятой? :thinking:

Если судить по IEEE-754 калькулятору, то действительно 100 представимо числом с плавающей точкой, но в процессе деления и умножения получаются числа, которые уже не представимы, поэтому берутся ближайшие. В итоге получаются не “чистые” 100 процентов. Ну а метод Floor отбрасывает всё после запятой, округляя до ближайшего меньшего целого. Округляйте до ближайшего целого с помощью Round.

Округляйте до ближайшего целого с помощью Round.

:fire:

Но тогда промежуток 99,5 - 99,99 будет округляться до 100, а я перфекционист. Не могу на такое смотреть :upside_down_face:

+0.01 → Floor → Round

А Round тогда тут зачем? :thinking: Уже ведь 0.01 прибавили и вниз округлили.

Не знаю, обнаружена неработоспособность головы, ушел в перезагрузку.

Там вторым параметром можно указать до какого знака округлять.
Math.Round Method (System) | Microsoft Learn

2 лайка

Да, точно. Не додумался до такого и не заметил, что у Round есть перегрузки :man_shrugging: