Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.62/34: Рейтинг темы: голосов - 34, средняя оценка - 4.62
-2 / 1 / 0
Регистрация: 18.07.2018
Сообщений: 84

Каков принцип округления дробных чисел у компьютера/компилятора?

25.07.2018, 23:01. Показов 7511. Ответов 88
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Всем привет.

У меня тут такой вопрос для темы: Как именно осуществляется округление дробных чисел в компьютере? Какими правилами он пользуется?
Я пишу программу, просто как тренировочную, в целях практики, так как я только начинаю путь программирования,ее цель (программы) это тренинг по математике, различного уровня сложности и головоломки, и вот в этой программе, возникли проблемы с расхождением результатов: результат, который ввожу я, (вычисленный на калькуляторе) отличается от результата, который вычисляет компьютер. При том что используемый калькулятор - стандартный в ОС Windows. Мало того значение в переменной округляется на разных этапах компиляции, что приводит в расхождению результатов и невозможно подобрать правильный. Такое ощущение, что компьютер играет со мной в стаканчики и крутит вертит эти результаты, чтобы запутать.

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

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

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


P.S. Пытался загрузить проект (правда он не закончен), но не получается пока... буду пытаться еще грузить, может кому интересно будет на него взглянуть.
0
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
25.07.2018, 23:01
Ответы с готовыми решениями:

Как избежать округления дробных чисел
как сделать что бы не округляло ??? float t; float z,m; using namespace std; void main(void) { m=-pow(5,2)/2;

Как избежать округления дробных чисел
не виводит дробь а только целие числа. где проблема ? #include <iostream.h> #include <math.h> int mat(double x,double...

Каков принцип работы атрибутов
Всем доброго времени суток. Объясните пожалуйста принцип работы атрибутов. То есть я написал атрибут и теперь я могу сериализовать...

88
-2 / 1 / 0
Регистрация: 18.07.2018
Сообщений: 84
25.07.2018, 23:34  [ТС]
Загрузился только .cpp файл, весь проект почему то не грузиться.
Вложения
Тип файла: zip MathTrainer.zip (10.7 Кб, 5 просмотров)
0
2784 / 1937 / 570
Регистрация: 05.06.2014
Сообщений: 5,602
25.07.2018, 23:55
Цитата Сообщение от txtbit Посмотреть сообщение
Как именно осуществляется округление дробных чисел в компьютере?
Число приводится в форму мантисса*2^экспонента, где мантисса и экспонента - целые числа. Мантисса имеет ограниченное количество двоичных знаков, те что не влезли - отбрасываются. Десятичные дроби в таком представлении становятся бесконечными, а потому у них знаки будут отбрасываться всегда. Разумеется, чем больше у вас операций дающих слишком много знаков в мантиссе, тем больше этих знаков будет выкинуто в процессе вычислений.

Впрочем, если вы о преобразовании double->int, то там просто обрубается дробная часть.
2
1394 / 1023 / 325
Регистрация: 28.07.2012
Сообщений: 2,813
26.07.2018, 00:17
txtbit, думаю, что в калькуляторе Windows используется длинная арифметика.
Цитата Сообщение от txtbit Посмотреть сообщение
Как именно осуществляется округление дробных чисел в компьютере?
Если мне память не изменяет, то обычно округление осуществляется до ближайшего представимого в данном формате числа. Однако алгоритм может отличаться в зависимости от платформы. Подробнее можешь глянуть в вики или еще где-нибудь.
1
зомбяк
 Аватар для TRam_
1585 / 1219 / 345
Регистрация: 14.05.2017
Сообщений: 3,940
26.07.2018, 00:19
Цитата Сообщение от txtbit Посмотреть сообщение
которые можно осуществить на уровне начальных знаний о С++
А знания математики на каком примерно уровне? Что такое логарифм уже изучал?
Цитата Сообщение от txtbit Посмотреть сообщение
компилятор округлил значение на каком то этапе компиляции
и калькулятор, и компьютер будут давать ошибку вычисления. "Абсолютно точно" они считают только если речь идёт строго о целых числах, причём если эти числа не получается поместить в типы данных, которые аппаратно обрабатываются процессором, применяется длинная арифметика (https://ru.wikipedia.org/wiki/Длинная_арифметика). Но, конечно, следует учитывать что делить нацело можно только некоторые числа, а остальные делятся с остатком (который либо отбрасывать, либо учитывать в качестве дроби, и т.д., но расчёты со всем этим будут идти очень медленно).

А по поводу чисел с плавающей точкой - да, там степень хранится для 2ки (в двоичной системе процессору легче считать), поэтому они изначально задаются не равными задаваемым десятичным дробям.
1
-2 / 1 / 0
Регистрация: 18.07.2018
Сообщений: 84
26.07.2018, 11:04  [ТС]
Всем спасибо за ответы, но видимо я не совсем точно выразил суть своего вопроса. Слишком мало конкретики в моем изложении сути, поэтому прогнал программу через отладчик и записал изменения всех вычислений на разных этапах, для облегчения более детального и главное визуального восприятия вопроса. Вписывать сюда примеры не стал будет очень много непонятных моментов, поэтому просто вложил файл с таблицей, где наглядно видно в чем заключается суть вопроса этой темы, и надеюсь есть ей решения...

Заранее всем спасибо.
Вложения
Тип файла: docx Пример Решений.docx (25.1 Кб, 11 просмотров)
0
-2 / 1 / 0
Регистрация: 18.07.2018
Сообщений: 84
26.07.2018, 11:21  [ТС]
Цитата Сообщение от Renji Посмотреть сообщение
Впрочем, если вы о преобразовании double->int, то там просто обрубается дробная часть.
Просто преобразовывать double в int конечно же я бы не стал, а если и стал, то подобных вопросов уже не задавал бы, иначе тут и так все понятно, куда деваются дробные знаки)))
0
зомбяк
 Аватар для TRam_
1585 / 1219 / 345
Регистрация: 14.05.2017
Сообщений: 3,940
26.07.2018, 11:37
txtbit, выводишь через cout ? Если да, то укажи сколько тебе требуется дробных знаков с помощью std::setprecision

https://en.cppreference.com/w/... tprecision

Если требуется большая точность, чем у double, то возьми например компилятор minGW, и используй тип long double (https://en.wikipedia.org/wiki/Long_double), т.к. компиляторы от microsoft работают с ним аналогично double.
0
-2 / 1 / 0
Регистрация: 18.07.2018
Сообщений: 84
26.07.2018, 11:56  [ТС]
Цитата Сообщение от TRam_ Посмотреть сообщение
выводишь через cout ?
Да, вывожу через "cout", работаю в Visual Studio 2017.

Цитата Сообщение от TRam_ Посмотреть сообщение
Если да, то укажи сколько тебе требуется дробных знаков с помощью std::setprecision
Ок, спасибо за совет, посмотрю в ближайшее время то это на std::setprecision.

Добавлено через 2 минуты
Сейчас перепроверял свои данные и у меня сомнения, что в первом примере я допустил ошибку в записи чисел, и написал неверное число, что привело в результате к расхождению результатов, но второй вариант точно составлен очень правильно, это проверено.
0
зомбяк
 Аватар для TRam_
1585 / 1219 / 345
Регистрация: 14.05.2017
Сообщений: 3,940
26.07.2018, 12:04
Лучший ответ Сообщение было отмечено txtbit как решение

Решение

Цитата Сообщение от txtbit Посмотреть сообщение
работаю в Visual Studio 2017
В ней есть только тип данных double, типа данных повышенной точности long double в ней нет.

Добавлено через 4 минуты
Цитата Сообщение от txtbit Посмотреть сообщение
но второй вариант точно составлен очень правильно
Если математику знаешь достаточно хорошо, попробуй вручную перевести свои числа в тот вид, в котором они содержатся в double.
https://ru.wikipedia.org/wiki/... й_точности
https://www.cyberforum.ru/blogs/18334/blog88.html
0
-2 / 1 / 0
Регистрация: 18.07.2018
Сообщений: 84
26.07.2018, 19:56  [ТС]
Цитата Сообщение от TRam_ Посмотреть сообщение
https://www.cyberforum.ru/blogs/18334/blog88.html
Данная статья все предельно и понятно изложила. Но возник теперь вопрос, что с этим делать...? Скорее всего будем изучать работу с std::setprecision.
0
зомбяк
 Аватар для TRam_
1585 / 1219 / 345
Регистрация: 14.05.2017
Сообщений: 3,940
26.07.2018, 20:04
Цитата Сообщение от txtbit Посмотреть сообщение
Но возник теперь вопрос, что с этим делать...?
Перевести числа в тот вид, в котором они вычисляются, и определить вручную, какие именно округления происходят в описанных тобою действиях.
Т.е. что будет округлено при переводе из твоих введённых чисел из нормированных по основанию 10 на нормированные с основанием 2, какие остатки будут отброшены при делении полученных чисел в формате double и т.д.

В общем проведи вычисления так, как проводит их процессор. Учитывая что целые значения процессор умеет считать точно, если они не переполняются.
0
Модератор
Эксперт по электронике
8978 / 6744 / 921
Регистрация: 14.02.2011
Сообщений: 23,852
26.07.2018, 20:08
txtbit, посмотри вот этот блог
https://www.cyberforum.ru/blogs/18334/blog88.html
Evg все доступно описал

Добавлено через 1 минуту
впрочем TRam_, уже дал на него ссылку

Добавлено через 3 минуты
Цитата Сообщение от txtbit Посмотреть сообщение
Скорее всего будем изучать работу с std::setprecision.
и что она тебе даст?
пойми что в компьютере данные хранятся в двоичном виде а std::setprecision выдает десятичный вид
так если введешь 0.1 то никогда не получишь этого значения поскольку 0.1 бесконечная дробь в двоичном представлении
0
2784 / 1937 / 570
Регистрация: 05.06.2014
Сообщений: 5,602
26.07.2018, 20:11
Цитата Сообщение от txtbit Посмотреть сообщение
Данная статья все предельно и понятно изложила. Но возник теперь вопрос, что с этим делать...?
Представлять числа как обыкновенные дроби и натравливать на них длинную арифметику. Правда, тормозить будет будь здоров.
0
-2 / 1 / 0
Регистрация: 18.07.2018
Сообщений: 84
26.07.2018, 20:44  [ТС]
Цитата Сообщение от Renji Посмотреть сообщение
Представлять числа как обыкновенные дроби и натравливать на них длинную арифметику. Правда, тормозить будет будь здоров.
Если честно, я понятия не имею о чем речь идет и как это вообще делать. Если есть литература, которая дает пояснения "на пальцах" с конкретными примерами, то было бы хорошо.

Добавлено через 2 минуты
Цитата Сообщение от ValeryS Посмотреть сообщение
и что она тебе даст?
пойми что в компьютере данные хранятся в двоичном виде а std::setprecision выдает десятичный вид
так если введешь 0.1 то никогда не получишь этого значения поскольку 0.1 бесконечная дробь в двоичном представлени
Возьму на заметку информацию про то что std::setprecision выдает десятичный вид. В будущем может где пригодиться...

Добавлено через 6 минут
Цитата Сообщение от TRam_ Посмотреть сообщение
Перевести числа в тот вид, в котором они вычисляются, и определить вручную, какие именно округления происходят в описанных тобою действиях.
Это поможет мне решить проблему со сравнением результата в переменной, отвечающей за хранение результата, и переменной, в которую пользователь вводит свой результат? Разница в том, что данные не совпадают, и откуда он берет эти данные я не знаю. Скорее всего я туплю по второму кругу, но суть именно в этом. Процессор не все числа записывает в "странную" для меня форму, а некоторые из них.. Как он выбирает что надо так сохранить, а что по другому... и как угадать когда и в каком виде будет сохранено то или иное значение, в этом вопрос, потому что именно это мешает сравнить результаты и завершить программу хеппи эндом.
0
Модератор
Эксперт по электронике
8978 / 6744 / 921
Регистрация: 14.02.2011
Сообщений: 23,852
26.07.2018, 20:49
Цитата Сообщение от txtbit Посмотреть сообщение
std::setprecision выдает десятичный вид.
std::setprecision не выдает десятичный вид, он выдает количество отображаемых знаков
вот здесь подробней
setprecision() и setiosflags()
а вот cout,без специальных флагов, выдает десятичный вид
0
-2 / 1 / 0
Регистрация: 18.07.2018
Сообщений: 84
26.07.2018, 20:58  [ТС]
Вот пример записи, хранения и вывода данных типа float (использую этот тип, как "тип" переменных), из конкретных примеров во время отладки программы.


-8.40939720е-06 данные хранимые в переменной "ResultLDN", которые вычислил процессор


-8.409397335238171е-6 данные вычисленные калькулятором Windows


8.4094е-06 данные, хранящиеся в переменной "ResultLDN", выведенные в консоль с помощью "std::cout".
0
зомбяк
 Аватар для TRam_
1585 / 1219 / 345
Регистрация: 14.05.2017
Сообщений: 3,940
26.07.2018, 21:08
Выведи
C++
1
std::cout<<setprecision(8)<<ResultLDN;
0
2784 / 1937 / 570
Регистрация: 05.06.2014
Сообщений: 5,602
26.07.2018, 21:09
Цитата Сообщение от txtbit Посмотреть сообщение
Если честно, я понятия не имею о чем речь идет и как это вообще делать. Если есть литература, которая дает пояснения "на пальцах" с конкретными примерами, то было бы хорошо.
Обыкновенные дроби и работа с ними - школьный учебник арифметики. Однако, если делать по школьному учебнику, числитель или знаменатель может не влезть в long, даже если сама дробь и небольшая. В этом месте вы должны побить большие числитель и знаменатель на отдельные разряды и умножать/складывать их столбиком. Это собственно длинная арифметика и есть.

Впрочем, если вам хочется поковыряться в матане, то можете заменить столбик на модулярную арифметику. Сразу предупреждаю - деление там является очень нетривиальной операцией (зато, сложение/вычитание/умножение очень быстрое).
0
Evg
Эксперт CАвтор FAQ
 Аватар для Evg
21281 / 8305 / 637
Регистрация: 30.03.2009
Сообщений: 22,660
Записей в блоге: 30
26.07.2018, 21:35
Лучший ответ Сообщение было отмечено ValeryS как решение

Решение

Чисто на всякий случай. Нужно чётко понимать, что "число" и "запись числа" - это несколько разные вещи

Цитата Сообщение от txtbit Посмотреть сообщение
-8.40939720е-06 данные хранимые в переменной "ResultLDN", которые вычислил процессор
Не так. ResultLDN - это переменная типа float, double, long double или какого-то ещё неведомо какого типа, если он у тебя есть. Внутри себя эта переменная хранит набор нулей и единиц, которые трактуются как некоторое вещественное число, хотя более корректно это нужно рассматривать не как число, а как запись числа в двоичном представлении, упакованном в специальный формат. Разумеется, в общем случае число представлено не точно, т.к. количество бит в типе конечно. Но чем больше бит у типа, тем более точно можно представить число (т.е. тем меньше значение этого числа будет отличаться от оригинала). Другими словами, число хранится в округлённом виде с мантиссой, представленной с точностью до скольких-то знаков после запятой В ДВОИЧНОЙ ЗАПИСИ. Чем больше бит в типе, тем больше число знаков после запятой

Далее при печати это число (запись числа в двоичной форме записи) конвертируется в десятичную форму записи. В общем случае десятичная форма записи будет иметь очень большое число знаков после запятой В ДЕСЯТИЧНОМ ВИДЕ, несмотря на то, что в двоичном виде число знаков после запятой совсем небольшое

В итоге "-8.40939720е-06" - это НЕ данные, хранимые в переменной "ResultLDN". Это их отображение в десятичном представлении, напечатанное с точностью до 8 десятичных знаков мантиссы после запятой

Ну и так, чтобы наглядно всё было перед глазами. Может быть, сообразишь, почему в разных программах на экран печатаются немного разные значения

C++
#include <iostream>
#include <iomanip>
 
// Число 1/3 в двоичной записи имеет бесконечное число
// знаков после запятой. Хранимое в двоичном виде число по сути дела
// округляется до скольких-то двоичных знаков. Чем шире тип, тем больше
// двоичных знаков после запятой, т.е. тем более точно хранимое значение
// будет приближено в реальному значению 1/3, но никогда его не достигнет
float f = 1.0f / 3.0f;
double d = 1.0 / 3.0;
long double ld = 1.0L / 3.0L;
 
int main ()
{
  // Неточно представленное в двоичном представлении число 1/3
  // конвертируется в десятичную запись. При этом в десятичной записи
  // печатаются только первые несколько знаков после запятой. Чем
  // больше знаков печатается, тем ближе напечатанное значение
  // к реально хранимому в переменной значению (которое само по
  // себе НЕ является точным)
  //
  // Можем заметить, что печать как бы одного и того же значения
  // начиная с некоторой точности выглядит по разному для разных
  // типов. Это обусловлено разными потерями точности именно
  // в процессе хранения. Если же мы возьмём число не 1/3, а 1/2,
  // то там таких различий не будет, поскольку 1/2 представляется
  // без потерь в двоичном виде. А при преобразовании в десятичную
  // форму записи имеет маленькое количество десятичных знаков
  // после запятой. Если возьмём число 1/65536, то различия увидим
  // только до нескольких знаков после запятой, а дальше результаты будут
  // совпадать. Это обусловлено тем, что значение в двоичном виде точное,
  // и при преобразовании в десятичную запись имеется сравнительно
  // небольшое количество знаков после запятой
 
  std::cout << std::setprecision (3) << f << std::endl;
  std::cout << std::setprecision (6) << f << std::endl;
  std::cout << std::setprecision (12) << f << std::endl;
  std::cout << std::setprecision (20) << f << std::endl;
  std::cout << std::setprecision (30) << f << std::endl;
 
  std::cout << std::endl;
 
  std::cout << std::setprecision (3) << d << std::endl;
  std::cout << std::setprecision (6) << d << std::endl;
  std::cout << std::setprecision (12) << d << std::endl;
  std::cout << std::setprecision (20) << d << std::endl;
  std::cout << std::setprecision (30) << d << std::endl;
 
  std::cout << std::endl;
 
  std::cout << std::setprecision (3) << ld << std::endl;
  std::cout << std::setprecision (6) << ld << std::endl;
  std::cout << std::setprecision (12) << ld << std::endl;
  std::cout << std::setprecision (20) << ld << std::endl;
  std::cout << std::setprecision (30) << ld << std::endl;
}
Code
0.333
0.333333
0.333333343267
0.3333333432674407959
0.3333333432674407958984375
 
0.333
0.333333
0.333333333333
0.33333333333333331483
0.333333333333333314829616256247
 
0.333
0.333333
0.333333333333
0.33333333333333333334
0.333333333333333333342368351437
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
26.07.2018, 21:35
Помогаю со студенческими работами здесь

Каков принцип работы парсера?
Здравствуйте! Можете какой нибудь парсер объяснить? Именно как он работает, можно даже продемонстрировать код с комментариями, буду очень...

Aspnet_regsql: каков принцип работы
Вопрос такого плана - пытаюсь добавить таблички с пользователями с помощью этой утилиты - если добавляю в БД на сервере то добавляется, но...

GZipstream - каков принцип многопоточного сжатия/распаковки?
Добрый день! Интересует тема сходная с https://www.cyberforum.ru/csharp-beginners/thread921121.html. Организовал многопоточное сжатие,...

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

Каков принцип работы самого простого web crawler?
Здравствуйте! Каков принцип работы самого простого web crawler? В Интернете нашел несколько вариантов: нужно сохранить все ссылки сайта,...


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

Или воспользуйтесь поиском по форуму:
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