Форум программистов, компьютерный форум, киберфорум
C# для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
5 / 3 / 2
Регистрация: 27.04.2022
Сообщений: 60

Точность чисел с плавающей точкой/запятой

10.05.2025, 02:32. Показов 3625. Ответов 42
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Добрый день!

Имеется массив чисел типа double от 1.000 до 10.000. Каждое число задано с точность до 10^-3. Также есть метод, который принимает число и возвращает ближайшее большее к нему число из этого массива. Всё работает хорошо, кроме одного числа - 1.6, которое всегда возвращается, собственно, не как 1.6, а как 1.6000000000000003. Я понимаю, что с числами с плавающей точкой/запятой не всё так просто, как может показаться на первый взгляд, но всё-таки:
- почему это так работает? Причём только с этим числом. Все остальные возвращаются такими, какими они заданы в массиве;
- возможно ли от этого избавиться без применения округлений, обрезаний и прочих инструментов?
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
10.05.2025, 02:32
Ответы с готовыми решениями:

Перевести десятичное число с плавающей запятой (точкой) в шестнадцатеричную систему и в 4-байтовом формате
Здравствуйте. Как перевести десятичное число с плавающей запятой (точкой) в шестнадцатиричную...

Генерация случайных чисел с плавающей точкой
Как сгенерировать случайное число на отрезке от, например, 1.2144 до 145.34213 ?

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

42
Native x86
Эксперт Hardware
 Аватар для quwy
6853 / 3787 / 1024
Регистрация: 13.02.2013
Сообщений: 11,861
10.05.2025, 03:07
Цитата Сообщение от power_factor Посмотреть сообщение
почему это так работает?
Потому что не любое вещественное число может быть точно представлено в двоичной системе исчисления. Вас же не удивляет, что 1/3 не имеет конечного точного значения в десятичной системе?

Цитата Сообщение от power_factor Посмотреть сообщение
Причём только с этим числом
Просто именно это число оказалось "неудачным" для FPU, как 1/3 для десятичной системы.

Цитата Сообщение от power_factor Посмотреть сообщение
возможно ли от этого избавиться без применения округлений, обрезаний и прочих инструментов?
Представлять число в другой форме (например, в виде текстовой строки), обрабатывать его не средствами процессорных инструкций, а своими функциями, что сожрет 99% производительности.
0
Эксперт .NET
 Аватар для Wolfdp
3788 / 1765 / 371
Регистрация: 15.06.2012
Сообщений: 6,543
Записей в блоге: 3
10.05.2025, 04:18
Цитата Сообщение от power_factor Посмотреть сообщение
почему это так работает?
Потому что 10 кратно 2 и 5, а бинарная система исчисления только 2. Из-за этого можно точно представить в двоичном виде только числа полученные путём деления на 2, например 0.5 или 0.125. В остальных случаях будем получать бесконечные дроби в двоичном формате, как например при 1/3 = 0.(3).

Не по теме:

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



Цитата Сообщение от power_factor Посмотреть сообщение
возможно ли от этого избавиться без применения округлений, обрезаний и прочих инструментов?
decimal

Работает медленней, но зато правильно работает в десятичном формате. Для финансов рекомендуют использовать этот формат. Для просчета графики в играх уже не шибко подходит, т.к. там нужно просчитывать очень много за очень короткое время.
1
5 / 3 / 2
Регистрация: 27.04.2022
Сообщений: 60
10.05.2025, 15:40  [ТС]
Цитата Сообщение от quwy Посмотреть сообщение
что сожрет 99% производительности.
Цитата Сообщение от Wolfdp Посмотреть сообщение
decimal
Работает медленней, но зато правильно работает в десятичном формате.
Стало быть, с точки зрения производительности применение Math.Round в данном случае будет самым верным решением?
0
Эксперт .NET
 Аватар для Wolfdp
3788 / 1765 / 371
Регистрация: 15.06.2012
Сообщений: 6,543
Записей в блоге: 3
10.05.2025, 16:34
Цитата Сообщение от power_factor Посмотреть сообщение
Стало быть, с точки зрения производительности применение Math.Round в данном случае будет самым верным решением?
С точки зрения чего? Массива чисел в вакууме? Вообще без понятия.

p.s. 99% это явно было не про decimal. Плюс вам нужна точность до третьего знака, а проблемы начинаются после шестнадцатого. Вам нужно будет очень много операций, чтобы накопить ощутимую погрешность.
0
5 / 3 / 2
Регистрация: 27.04.2022
Сообщений: 60
10.05.2025, 17:20  [ТС]
Цитата Сообщение от Wolfdp Посмотреть сообщение
С точки зрения чего?
С точки зрения, что округлять double рациональнее, чем хранить числа с точность до 3 знаков в decimal или парсить из строк в числа и обратно, как мне видится.
0
Эксперт JavaЭксперт по электроникеЭксперт .NET
 Аватар для wizard41
3384 / 2703 / 573
Регистрация: 04.09.2018
Сообщений: 8,511
Записей в блоге: 3
10.05.2025, 17:36
power_factor, округлять не рационально в этом случае.
Дробные числа принято сравнивать с некоторой, заранее установленной точностью.
Округлением числа для "показа" занимается форматирование вывода, оставляя при этом само дробное число не измененным.

Добавлено через 1 минуту
Более того - округление округлению рознь. Либо округлять по всем математическим законам, либо просто нужно отбросить n-ное кол-во знаков после запятой..
0
Эксперт .NET
 Аватар для Wolfdp
3788 / 1765 / 371
Регистрация: 15.06.2012
Сообщений: 6,543
Записей в блоге: 3
10.05.2025, 18:52
power_factor, Nope.

Есть конкретная прикладная задача, и под неё выбирают подходящий тип данных. Примеры указывал выше. Универсального решения не существует, в противном случае у нас бы существовал единый формат с плавающей точкой.
0
Эксперт JavaЭксперт по электроникеЭксперт .NET
 Аватар для wizard41
3384 / 2703 / 573
Регистрация: 04.09.2018
Сообщений: 8,511
Записей в блоге: 3
11.05.2025, 01:48
power_factor,
C#
1
2
3
4
5
6
7
8
// GT (a>b)?
bool GreaterThan(double a, double b, double eps)
{
    return (a - b) > eps;
}
 
Console.WriteLine(GreaterThan(1.6010000000000003, 1.6, 1E-3));  // True
Console.WriteLine(GreaterThan(1.6001000000000003, 1.6, 1E-3));  // False
0
5 / 3 / 2
Регистрация: 27.04.2022
Сообщений: 60
11.05.2025, 15:20  [ТС]
Цитата Сообщение от wizard41 Посмотреть сообщение
Более того - округление округлению рознь. Либо округлять по всем математическим законам, либо просто нужно отбросить n-ное кол-во знаков после запятой..
Например, мне нужно отбросить все знаки после запятой, начиная после шестого. Это делается так или есть более изящное решение?
C#
1
Console.WriteLine(Math.Truncate(1.600000000000003 * 1e6) / 1e6);
0
Эксперт JavaЭксперт по электроникеЭксперт .NET
 Аватар для wizard41
3384 / 2703 / 573
Регистрация: 04.09.2018
Сообщений: 8,511
Записей в блоге: 3
11.05.2025, 16:28
power_factor, это бессмысленные операции. Зачем это надо?
0
2393 / 1913 / 763
Регистрация: 27.07.2012
Сообщений: 5,557
11.05.2025, 17:14
Цитата Сообщение от power_factor Посмотреть сообщение
Например, мне нужно отбросить все знаки после запятой, начиная после шестого. Это делается так или есть более изящное решение?
Если для отображения, то есть варианты:
C#
1
2
Console.WriteLine("{1.600000000000003:G6}"); // 1.6
Console.WriteLine("{1.600000000000003:F6}"); // 1.600000
Добавлено через 26 минут
Более явным образом если:
C#
1
2
3
4
5
double x = 1.600000000000003;
 
Console.WriteLine(x.ToString("G6")); // 1.6
Console.WriteLine(x.ToString("F6")); // 1.600000
Console.WriteLine(x.ToString("E6")); // 1.600000E+000
0
Эксперт .NET
 Аватар для Wolfdp
3788 / 1765 / 371
Регистрация: 15.06.2012
Сообщений: 6,543
Записей в блоге: 3
11.05.2025, 17:58
Цитата Сообщение от power_factor Посмотреть сообщение
или есть более изящное решение?
Да, называется форматирование отображения. Выше примеры скинули. Спецом округлять значения для этого не нужно.
0
2393 / 1913 / 763
Регистрация: 27.07.2012
Сообщений: 5,557
11.05.2025, 18:27
Есть, кстати, ещё такой формат:
C#
1
2
3
double x = 1.600000000000003;
 
Console.WriteLine(x.ToString("#.######")); // 1.6
Возможно, это больше подходит ТС. В случае какого-то числа меньше 10^-6 выведет просто 0, а не экспоненциальную форму.
0
5 / 3 / 2
Регистрация: 27.04.2022
Сообщений: 60
11.05.2025, 22:50  [ТС]
Цитата Сообщение от John Prick Посмотреть сообщение
Если для отображения, то есть варианты.
Цитата Сообщение от Wolfdp Посмотреть сообщение
Да, называется форматирование отображения.
Console.WriteLine() указал неверно, нужно не для отображения. Метод возвращает значение, которое дальше может передаваться и использоваться в куче других методов. Мне думается, что если методы через раз будут возвращать, условно, не 1.6, а 1.600000000000003, то при большом количество вычислений эта "ошибка" может незаметно копиться и в конечном счёте привести к непредсказуемым результатам. Вот я и хочу на выходе из каждого метода "обрезать" лишнее, чтобы контролировать процесс. Насколько это правильный подход?
0
2393 / 1913 / 763
Регистрация: 27.07.2012
Сообщений: 5,557
11.05.2025, 23:29
Цитата Сообщение от power_factor Посмотреть сообщение
Мне думается, что если методы через раз будут возвращать, условно, не 1.6, а 1.600000000000003, то при большом количество вычислений эта "ошибка" может незаметно копиться
С таким же успехом методы могут выдавать через раз 1.599999999999997. В конечном итоге результат будет в пределах допустимой погрешности.

Более того, даже если вы округлите или как-то приведёте выход своего метода к нужному вам числу, точно такие же "ошибки" float-вычислений могут появиться в вашей последующей "куче методов". Это борьба с ветряными мельницами.

Ну и если всё равно нет покоя, то надо проводить тесты и смотреть, когда и при каких условиях эта ошибка может накопиться и стать хоть сколько-нибудь заметной. Тогда уже предпринимать какие-либо действия. Или не предпринимать.
0
Эксперт .NET
 Аватар для Wolfdp
3788 / 1765 / 371
Регистрация: 15.06.2012
Сообщений: 6,543
Записей в блоге: 3
12.05.2025, 00:18
Цитата Сообщение от power_factor Посмотреть сообщение
Мне думается, что если методы через раз будут возвращать, условно, не 1.6, а 1.600000000000003, то при большом количество вычислений эта "ошибка" может незаметно копиться и в конечном счёте привести к непредсказуемым результатам.
Думается правильно, но у вас точность е-3, погрешность возникает на е-16(!) Т.е. вам нужно допустим выполнить более триллиона(!) операций сложения, чтобы достичь ощутимых результатов. У вас есть такое количество операций?

Именно поэтому я говорю что "нужно отталкиваться от задачи", а не пытаться найти идеально решение для всех случаев. Если бы оно существовало, давно бы уже всем было известно и применялось.
0
Заблокирован
12.05.2025, 01:52
Цитата Сообщение от power_factor Посмотреть сообщение
Вот я и хочу на выходе из каждого метода "обрезать" лишнее, чтобы контролировать процесс.
Но как вам сказали в начале темы, что множество десятичных чисел не может точно представляться в двоичном виде.
Ваше число уже и так максимально близкое к исходному.
Ничего предпринимать не нужно.

Цитата Сообщение от power_factor Посмотреть сообщение
при большом количество вычислений эта "ошибка" может незаметно копиться
И заметно тоже.
Ага. Чудеса математики.
0
151 / 135 / 29
Регистрация: 02.07.2013
Сообщений: 962
12.05.2025, 08:01
Цитата Сообщение от power_factor Посмотреть сообщение
не 1.6, а 1.600000000000003
а как вы будите проводить коррекцию числа (округление). вам же все равно компьютер после округления выдаст 1.600000000000003.

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

так же не забывайте что есть и ошибки типа переполнения, или есть явление расширения до int32 (размера регистра) при промежуточных вычислениях и наверняка еще много всего с чем можно столкнуться.
0
Эксперт JavaЭксперт по электроникеЭксперт .NET
 Аватар для wizard41
3384 / 2703 / 573
Регистрация: 04.09.2018
Сообщений: 8,511
Записей в блоге: 3
12.05.2025, 10:55
На самом деле, гипотетически обеспечить приемлемую точность - нормальное желание, однако здесь нужно не вдаваться в "фанатизм" и действовать в рамках разумного..
Так, на пальцах может и не совсем понятно, поэтому проще привести более-менее реальный пример. Немного посчитаем:

Предположим, у нас есть ролик (барабан) диаметром 1м, и мы по нему прокатываем бесконечную веревку, делая при этом ровно 1 миллион оборотов. Мы хотим точно узнать - какую длину этой веревки мы протащили через барабан.

Для начала, для вычисления длины окружности барабана мы возьмем ПИ с 6-ю знаками: 3,141592
т.о. L = 3.141592 * 1 = 3.141592 метра / оборот.
Миллион оборотов: 3.141592 * 106 = 3 141 592 метра.

Теперь берем ПИ с 10-ю знаками: 3,1415926535
L = 3,1415926535 метра / оборот.
Миллион оборотов: 3.1415926535 * 106 = 3 141 592.6535 метра.

Погрешность составила около 2,08 * 10-7 или всего 2,08 * 10-5%.
Для такой дистанции это весьма очень мало!

Поэтому, например, в небесной механике или в астрономических расчетах (траекторий полетов, орбит) принимают всего 5-6 знаков числа ПИ - этого достаточно вполне. Погрешность настолько мала, что ей можно пренебречь..
1
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
12.05.2025, 10:55
Помогаю со студенческими работами здесь

Хранение чисел с плавающей запятой в памяти
Здравствуйте. Скорее, вопрос не по теме, просто не знаю, куда задать вопрос. Вопрос связан с...

Формат представления чисел с плавающей запятой
Передаю число с плавающей запятой через COM на МК 0,0043 но в итоге получаю на самом МК число...

число с плавающей точкой
подскажите как из строкого формата числа с двумя числами после запитой, например 122,00 или 32,94,...

Числа с плавающей точкой
Как отделить целую часть и дробную? Например имеем число 12.54 число 12 должно бить записано в...

Введение данных с плавающей точкой в TextBox
Такая проблема: на форме есть 2 текстбокса, в один должно вводится число, которое будем приводить к...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Новые блоги и статьи
PhpStorm 2025.3: WSL Terminal всегда стартует в ~
and_y87 14.12.2025
PhpStorm 2025. 3: WSL Terminal всегда стартует в ~ (home), игнорируя директорию проекта Симптом: После обновления до PhpStorm 2025. 3 встроенный терминал WSL открывается в домашней директории. . .
Access
VikBal 11.12.2025
Помогите пожалуйста !! Как объединить 2 одинаковые БД Access с разными данными.
Новый ноутбук
volvo 07.12.2025
Всем привет. По скидке в "черную пятницу" взял себе новый ноутбук Lenovo ThinkBook 16 G7 на Амазоне: Ryzen 5 7533HS 64 Gb DDR5 1Tb NVMe 16" Full HD Display Win11 Pro
Музыка, написанная Искусственным Интеллектом
volvo 04.12.2025
Всем привет. Некоторое время назад меня заинтересовало, что уже умеет ИИ в плане написания музыки для песен, и, собственно, исполнения этих самых песен. Стихов у нас много, уже вышли 4 книги, еще 3. . .
От async/await к виртуальным потокам в Python
IndentationError 23.11.2025
Армин Ронахер поставил под сомнение async/ await. Создатель Flask заявляет: цветные функции - провал, виртуальные потоки - решение. Не threading-динозавры, а новое поколение лёгких потоков. Откат?. . .
Поиск "дружественных имён" СОМ портов
Argus19 22.11.2025
Поиск "дружественных имён" СОМ портов На странице: https:/ / norseev. ru/ 2018/ 01/ 04/ comportlist_windows/ нашёл схожую тему. Там приведён код на С++, который показывает только имена СОМ портов, типа,. . .
Сколько Государство потратило денег на меня, обеспечивая инсулином.
Programma_Boinc 20.11.2025
Сколько Государство потратило денег на меня, обеспечивая инсулином. Вот решила сделать интересный приблизительный подсчет, сколько государство потратило на меня денег на покупку инсулинов. . . .
Ломающие изменения в C#.NStar Alpha
Etyuhibosecyu 20.11.2025
Уже можно не только тестировать, но и пользоваться C#. NStar - писать оконные приложения, содержащие надписи, кнопки, текстовые поля и даже изображения, например, моя игра "Три в ряд" написана на этом. . .
Мысли в слух
kumehtar 18.11.2025
Кстати, совсем недавно имел разговор на тему медитаций с людьми. И обнаружил, что они вообще не понимают что такое медитация и зачем она нужна. Самые базовые вещи. Для них это - когда просто люди. . .
Создание Single Page Application на фреймах
krapotkin 16.11.2025
Статья исключительно для начинающих. Подходы оригинальностью не блещут. В век Веб все очень привыкли к дизайну Single-Page-Application . Быстренько разберем подход "на фреймах". Мы делаем одну. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2025, CyberForum.ru