powered by simpleCommunicator - 2.0.59     © 2025 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / C++ [игнор отключен] [закрыт для гостей] / fmod(1.0, 0.2)=0.2
25 сообщений из 40, страница 1 из 2
fmod(1.0, 0.2)=0.2
    #39206842
.NET
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Почему функция fmod из стандартной библиотеки (реализация MS VS 2013) возвращает 0.2 при входных параметрах 1.0 и 0.2?
Несмотря на то, что MSDN сообщает:

https://msdn.microsoft.com/en-us/library/20dckbeh%28v=vs.120%29.aspx]The fmod function calculates the floating-point remainder f of x / y such that x = i * y + f, where i is an integer, f has the same sign as x, and the absolute value of f is less than the absolute value of y .


Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
#include <cmath>
#include <iostream>

using namespace std;

int main()
{
	double num = 1.0;
	double denum = 0.2;
	cout << num - floor(num/denum) * denum << " "  << fmod(num, denum);

	cin.get();

	return 0;
}
...
Рейтинг: 0 / 0
fmod(1.0, 0.2)=0.2
    #39206852
Dimitry Sibiryakov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
.NETвозвращает 0.2 при входных параметрах 1.0 и 0.2?
Потому что ты выводишь мало цифр после запятой. То, что она возвращает не совсем 0.2. Да и
то, на что она делит, собственно говоря, тоже.
Posted via ActualForum NNTP Server 1.5
...
Рейтинг: 0 / 0
fmod(1.0, 0.2)=0.2
    #39206872
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
.NET,

Число 0.2 точно нельзя представить в виде float/double.
Поэтому в зависимости от реализации будет выбрано либо чуть меньшее либо чуть большее число.
А поскольку fmod обычно реализован с помощью итеративных вычитаний (алгоритм Евклида), а не просто делением с усечением до целого, то вполне возможны разные результаты.
...
Рейтинг: 0 / 0
fmod(1.0, 0.2)=0.2
    #39206919
.NET
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Да, я понял.
Если вывести больше цифр, то получится 1.999999 и т. д.
Однако, 0.2 * 5 все же равно 1.
Почему в этом случае 0.2 трактуется как 0.2 в точности?
...
Рейтинг: 0 / 0
fmod(1.0, 0.2)=0.2
    #39206921
чччД
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
.NET...Почему в этом случае 0.2 трактуется как 0.2 в точности?

Тебя смущает, что одно неточное представление 0.2 10 совпадает другим, сформированным точно так же, неточным представлением 0.2 10 ?
...
Рейтинг: 0 / 0
fmod(1.0, 0.2)=0.2
    #39206938
Фотография Usman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
...
Рейтинг: 0 / 0
fmod(1.0, 0.2)=0.2
    #39206946
.NET
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
чччД.NET...Почему в этом случае 0.2 трактуется как 0.2 в точности?

Тебя смущает, что одно неточное представление 0.2 10 совпадает другим, сформированным точно так же, неточным представлением 0.2 10 ?
Если остаток от деления 1 на 0.2 10 даёт не 0, а 1.9999... , то значит 0.2 2 > 0.2,
почему же тогда 0.2 10 * 5 = 1, а не больше?

Вообще непонятно зачем нужна функция нахождения остатка от деления для вещественных чисел,
тем более что она возвращает такие неожиданные значения.
Почему не ограничится целыми.
...
Рейтинг: 0 / 0
fmod(1.0, 0.2)=0.2
    #39206969
alexy_black
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
то, что .2 * 5 == 1 не означает что .2 это именно .2 а не какое-нибудь другое число..

эта арифметика так и называется - плавающая :)
...
Рейтинг: 0 / 0
fmod(1.0, 0.2)=0.2
    #39207054
чччД
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
.NET...почему же тогда...

Потому что ты не знаешь, как устроены double-precision floating-point format числа.
Разбирайся.
...
Рейтинг: 0 / 0
fmod(1.0, 0.2)=0.2
    #39207073
Пётр Седов
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
.NETОднако, 0.2 * 5 все же равно 1.
Почему в этом случае 0.2 трактуется как 0.2 в точности?Число 0.2 записывается бесконечным количеством цифр в двоичной системе счисления:
0.2 10 = 0.00110011001100110011... 2
(маленькие числа внизу -- это основание системы счисления)
Но в мантиссе типа double только 53 двоичных цифры, поэтому:

Число 0.2 не попадает в точности ни в одно double-число, оно попадает между двумя double-числами, x1 и x2:
Код: plaintext
1.
x1 = 1.1001100110011001100110011001100110011001100110011001*2 -3  = 0.1999999999999999833466546306226518936455249786376953125 (в точности)
x2 = 1.1001100110011001100110011001100110011001100110011010*2 -3  = 0.200000000000000011102230246251565404236316680908203125 (в точности)
(x1 < 0.2 < x2)
0.2 ближе к x2, чем к x1, поэтому именно x2 выбирается для представления 0.2 (можете проверить с помощью printf("0.2 ≈ %.60f\n", 0.2)).

Число y = 5 * x2 не попадает в точности ни в одно double-число, оно попадает между двумя double-числами, z1 и z2:
Код: plaintext
1.
2.
z1 = 1.0000000000000000000000000000000000000000000000000000*2 0  = 1.00000000000000000 (в точности)
y                                                              = 1.000000000000000055511151231257827021181583404541015625 (в точности)
z2 = 1.0000000000000000000000000000000000000000000000000001*2 0  = 1.0000000000000002220446049250313080847263336181640625 (в точности)
(z1 < y < z2)
y ближе к z1, чем к z2, поэтому именно z1 выбирается в качестве результата умножения.

Вот так и получается, что неточное 0.2 (x2) умножаем на 5, и получаем точную единицу (z1). Вообще, floating point арифметика работает так, как будто сначала вычисляется результат с идеальной точностью, а потом:
* Если идеальный результат попадает в точности в какое-нибудь double-число, то оно и берётся в качестве double-результата. Поэтому например работа с целыми числами в достаточно большом диапазоне идёт без погрешности:
Код: plaintext
1.
2.
3.
double x = 1;
double y = 2;
assert(x + y == 3);

* Если идеальный результат попадает между двумя double-числами, то ближайшее из них выбирается в качестве double-результата. Если идеальный результат ровно посередине между двумя double-числами, то выбирается то, у которого младшая двоичная цифра -- 0 (round to even).
...
Рейтинг: 0 / 0
fmod(1.0, 0.2)=0.2
    #39207085
Фотография MasterZiv
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
.NET,
а нафига тебе остаток от деления нецелых чисел вообще?

смысл в этом какой?
...
Рейтинг: 0 / 0
fmod(1.0, 0.2)=0.2
    #39207230
.NET
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Пётр Седов,
Спасибо, за подробный ответ.
Т. е. если вкратце, то ошибка в представлении 0.2 настолько мала, что да же будучи умноженная на 5 её не хватает на то чтобы изменить
последний разряд результата умножения.

MasterZiv,
Несколькими постами выше я сам задавался подобным вопросом.
Зачем нужен остаток от деления нецелых?
Мне пока незачем. Я изучаю язык.

Но чисто гипотетически,
если надо разлить бочку воды ёмкости 87.9 литра по вёдрам ёмкостью 9.7 литра
и узнать сколько останется, то почему бы и нет.
...
Рейтинг: 0 / 0
fmod(1.0, 0.2)=0.2
    #39207269
Фотография Изопропил
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
.NETесли надо разлить бочку воды ёмкости 87.9 литра по вёдрам ёмкостью 9.7 литра
и узнать сколько останется, то почему бы и нет.
Деньги не вздумай так считать
...
Рейтинг: 0 / 0
fmod(1.0, 0.2)=0.2
    #39207368
Фотография MasterZiv
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
.NETНесколькими постами выше я сам задавался подобным вопросом.
Зачем нужен остаток от деления нецелых?
Мне пока незачем. Я изучаю язык.


Ага, и ты начал именно с fmod ...
Да я всё жизнь (не, пол) этот язык изучаю, но никогда про эту функцию не слышал даже.
...
Рейтинг: 0 / 0
fmod(1.0, 0.2)=0.2
    #39207378
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
.NETНо чисто гипотетически,
если надо разлить бочку воды ёмкости 87.9 литра по вёдрам ёмкостью 9.7 литра
и узнать сколько останется, то почему бы и нет.
...
Рейтинг: 0 / 0
fmod(1.0, 0.2)=0.2
    #39207405
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Тут смотря в какую сторону считать
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
	double denum = 0.2;
	double test = 0.;
	printf("up from %e\n", test);
	for (int i = 0; i < 5; i++) {
		test += denum;
		printf("%e => %llX \n", test, test);
	}
	test = 1.;
	printf("down from %e\n", test);
	for (int i = 0; i < 5; i++) {
		test -= denum;
		printf("%e => %llX \n", test, test);
	}

результатup from 0.000000e+00
2.000000e-01 => 3FC999999999999A
4.000000e-01 => 3FD999999999999A
6.000000e-01 => 3FE3333333333334
8.000000e-01 => 3FE999999999999A
1.000000e+00 => 3FF0000000000000
down from 1.000000e+00
8.000000e-01 => 3FE999999999999A
6.000000e-01 => 3FE3333333333334
4.000000e-01 => 3FD999999999999B
2.000000e-01 => 3FC999999999999C
5.551115e-17 => 3C90000000000000
При уменьшении ошибка накапливается быстрее.
...
Рейтинг: 0 / 0
fmod(1.0, 0.2)=0.2
    #39207785
.NET
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
MasterZiv.NETНесколькими постами выше я сам задавался подобным вопросом.
Зачем нужен остаток от деления нецелых?
Мне пока незачем. Я изучаю язык.


Ага, и ты начал именно с fmod ...
Да я всё жизнь (не, пол) этот язык изучаю, но никогда про эту функцию не слышал даже.
Если говорить не о языке C вообще, а только о функциях стандартной
библиотеки, то начал я, если вам интересно, с функций объявленных
в заголовочном файле ctype.h. Но там все более менее понятно, так
что необходимости тревожить уважаемое сообщество не возникло.
...
Рейтинг: 0 / 0
fmod(1.0, 0.2)=0.2
    #39207811
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
.NET, для старожилов этого форума, обсуждение дефекта вычислений с IEEE 754 давно уже
набило оскомину. И я понимаю досаду с которой мы восприняли твой вопрос. Просто ответ
на него не лежит в плоскости того как работает fmod() которую я тоже никогда не использовал.
Подозреваю что ее определение тривиально.

Но тебя бьют и кусают не за fmod а за незнания fundamentals.
...
Рейтинг: 0 / 0
fmod(1.0, 0.2)=0.2
    #39207852
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
.NET,

я немного пропустил в коде 19013898 , начало было такое
Код: plaintext
1.
2.
3.
4.
5.
	double num = 1.0;
	double denum = 0.2;
	double res = fmod(num, denum);
	cout << res << endl;
	cout << ((res < denum) ? "res < denum" : "res >= denum") << "  delta: " << denum - res << endl;


результат0.2
res < denum delta: 5.55112e-17
ожидаемо что чуть не сошлось в 17 разряде. Потому что точность double 15-16 десятичных разрядов.

Надо знать основы. Вычисления типов с плавающей запятой имеют погрешности, надо это учитывать.

PS Про fmod() тоже не знал, бесполезная функция в связи с выше описанным.

PPS Изопропил, деньги в double считать можно. 15 десятичных разрядов достаточно чтобы посчитать до 100 триллионов по копейке. ЕМНИП уже писал именно тебе о том что погрешности вылезают если уйти от сложения и умножения на целое, а если уйти они все равно вылезают хоть с double хоть с int/decimal/money. Тут главное прямое сравнение не использовать.
...
Рейтинг: 0 / 0
fmod(1.0, 0.2)=0.2
    #39207903
Фотография Usman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Usman.NET,

Using Boost.Multiprecision
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
#include <iostream>
#include <boost/math/constants/constants.hpp>
#include <boost/multiprecision/cpp_dec_float.hpp>

using boost::multiprecision::cpp_dec_float_50;

int main() {
    double num = 1.0;
    double denum = 0.2;
    
    cpp_dec_float_50 f_num = cpp_dec_float_50(num);
    cpp_dec_float_50 f_denum = cpp_dec_float_50(denum);
    cpp_dec_float_50 f_result = boost::multiprecision::fmod(f_num, f_denum);

    std::cout.precision(std::numeric_limits<cpp_dec_float_50>::digits10);
    std::cout << f_result << std::endl;

    return 0;
}

Вывод:
Код: plaintext
1.
0.19999999999999995559107901499373838305473327636719
...
Рейтинг: 0 / 0
fmod(1.0, 0.2)=0.2
    #39207917
Фотография Usman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Без double :
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
#include <iostream>
#include <boost/math/constants/constants.hpp>
#include <boost/multiprecision/cpp_dec_float.hpp>

using boost::multiprecision::cpp_dec_float_50;

int main() {
    cpp_dec_float_50 f_two = cpp_dec_float_50(2);
    cpp_dec_float_50 f_ten = cpp_dec_float_50(10);

    cpp_dec_float_50 f_num = cpp_dec_float_50(1);
    cpp_dec_float_50 f_denum = cpp_dec_float_50(f_two / f_ten);
    cpp_dec_float_50 f_result = boost::multiprecision::fmod(f_num, f_denum);

    std::cout.precision(std::numeric_limits<cpp_dec_float_50>::max_digits10);
    std::cout << "f_num    = " << f_num << std::endl;
    std::cout << "f_denum  = " << f_denum << std::endl;
    std::cout << "f_result = " << f_result << std::endl;

    return 0;
}

Код: plaintext
1.
2.
3.
f_num    = 1
f_denum  = 0.2
f_result = 0
...
Рейтинг: 0 / 0
fmod(1.0, 0.2)=0.2
    #39207966
Пётр Седов
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dima T
Код: plaintext
1.
2.
3.
double test = 0.;
...
printf("%e => %llX \n", test, test);

В C99 можно выводить floating point числа без округления, через %a. Мантисса пишется 16-ричными цифрами, потом буква p, потом степень двойки.
Код: plaintext
1.
2.
printf("128 = %a\n", 128.0);
printf("0.2 &#8776; %a\n", 0.2);

вывод:
Код: sql
1.
2.
128 = 0x1p+7
0.2 &#8776; 0x1.999999999999ap-3

1.999999999999a 16 *2 -3 = 0001.1001'1001'1001'1001'1001'1001'1001'1001'1001'1001'1001'1001'1010 2 *2 -3 = x2 (из моего первого сообщения)

Usman Using Boost.Multiprecision Чем это лучше/хуже GMP ?

MasterZivДа я всё жизнь (не, пол) этот язык изучаю,Задумался, сколько лет MasterZiv-у. Сам-то он не помнит .
...
Рейтинг: 0 / 0
fmod(1.0, 0.2)=0.2
    #39207967
Фотография Usman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Пётр СедовUsman Using Boost.Multiprecision Чем это лучше/хуже GMP ?Нужно сравнить. Будем ждать примеров на GMP.
...
Рейтинг: 0 / 0
fmod(1.0, 0.2)=0.2
    #39208020
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Я после 26 тоже перестал сообщать свой возраст. Наверное внутренний философ заговорил.

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



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

По чесноку. Можно даже дать два ответа. Один - для аудитории а другой для себя.
...
Рейтинг: 0 / 0
fmod(1.0, 0.2)=0.2
    #39208053
Фотография SashaMercury
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Марк, все эти кривые очень индивидуальны. У людей, занятых интеллектуальным трудом (а хорошие программисты этим и занимаются (не все программисты - хорошие, конечно)), всё это происходит совершенно по другому, чем у людей работающих на обычной скучной и нудной работе
...
Рейтинг: 0 / 0
25 сообщений из 40, страница 1 из 2
Форумы / C++ [игнор отключен] [закрыт для гостей] / fmod(1.0, 0.2)=0.2
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]