LINUX.ORG.RU

[непонимание]почемуже оно так?!

 


0

1

заинтересовала одна вещь, а именно: имеем код

float f=0;
    int i;
    for(i=0;i<100000;i++) f+=0.1;

почему же вместо ожидаемых 10000 получаю 9998.556640625?? компилил на армах, интелах и амд. в шиндошс и линуксе, результат один. что не так-то? из-за чего столь большая погрешность???

Перемещено hibou из talks


Потому что в общем случае нельзя точно представить десятичное число в формате с плавающей точкой.

SoulThreads
()

20 лет назад, в школе (УПК) на 1 уроке по программированию рассказывали про анализ погрешностей при работе с действительными числами. Интересно, что рассказывают сейчас...

inoremap ★★
()

Краткий ответ: потому что не знаком с матчастью.

Запиши 0.1 в двоичной системе счисления.

geekless ★★
()
Ответ на: комментарий от inoremap

Интересно, что рассказывают сейчас...

Вероятно, как переустановить ШINDOШS.

geekless ★★
()
Ответ на: комментарий от inoremap

Рассказывают и это сейчас.

Что только не рассказывать, к сожалению, студенты ничего не запоминают, что поделать... Сейчас во всяком случае (может, раньше по-другому было)

BattleCoder ★★★★★
()

однако, спасибо за ответы. стал немного умней (:

abyz
() автор топика

Потому что ты пользуешься простой точностью.

dogbert ★★★★★
()

почему же вместо ожидаемых 10000 получаю 9998.556640625??

Я тебе сейчас фокус покажу:


y@gentoo-linux /var/tmp/portage $ cat 1.c 
#include <stdio.h>
void main()
{
float a=1.1;
printf("%0*.*f\n",12,10,a);
}
y@gentoo-linux /var/tmp/portage $ gcc 1.c 
y@gentoo-linux /var/tmp/portage $ ./a.out 
1.1000000238

ymuv ★★★★
()

Это сговор производителей процессоров. Чтобы обсчитывать честное население.

PolarFox ★★★★★
()

почемуже

«Патамушта_для_энтого_есь» C-XSC :)

quickquest ★★★★★
()

0.1 не представим в виде бинарной дроби.

Deleted
()

1/10 не разложим в виде конечного многочлена со степенями двойки.
Например, 1/8 = 2^{-3} (всё отлично)
1/16 = 1^{-4} (тоже отлично)
1/16 < 1/10 < 1/8 (не отлично)

pacify ★★★★★
()
float f=0;
int i;
for(i=0;i<20000;i++) f+=0.5;

Все должно работать.

segfault ★★★★★
()
Ответ на: комментарий от BattleCoder

Ха, все именно так, как получилось, а не так как должно быть.
Должен быть правильный ответ, а не оправдания, почему это не так.

anonymous_sapiens ★★★★★
()
Ответ на: комментарий от anonymous_sapiens

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

zloelamo ★★★★
()
Ответ на: комментарий от anonymous_sapiens

Должен быть правильный ответ, а не оправдания, почему это не так.

Т.е. фишка в том, что это и есть правильный ответ :)

zloelamo ★★★★
()

perl -e 'printf(«%d\n», 2.01*100)'

Ещё фокус.

Xellos ★★★★★
()
Ответ на: комментарий от zloelamo

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

anonymous_sapiens ★★★★★
()
Ответ на: комментарий от anonymous_sapiens

нет, это неправильный ответ.

Почему? Процессору сказали: «Вот тебе число 0,0001100110011... - сложи его n раз». Он и сложил.

Аналогично, я тебе предложу: «Сложи мне n раз число 0,(3)». Какое число у тебя получится не пользуясь простыми дробями?

zloelamo ★★★★
()
Ответ на: комментарий от zloelamo

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

anonymous_sapiens ★★★★★
()
Ответ на: комментарий от anonymous_sapiens

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

Ява не сильно абстрагирована. Я имел ввиду что-нибудь типа gnu octave.

zloelamo ★★★★
()

Ээээ, нам это еще на первом курсе на информатике рассказывали, а знал я это еще в школе

xorik ★★★★★
()
Ответ на: комментарий от anonymous_sapiens

ну причем тут процессор?
я сказал джаве/с/etc сложи мне несколько чисел.

Действительно, и при чем здесь процессор, ведь считает компилятор!!! :D

Эти языки созданы для упрощения разработки, ведь быстрее писать на C, чем на ассемблере, но в основе у них бинарная система счисления и машинная арифметика (раздел дискретной математики, кстати). И с точки зрения дискретной математики результат абсолютно верен.

Твое недоумение связано со способом записи a=0.1+0.1, который ты ошибочно связал с простой арифметикой, но этот способ записи выбран из соображений простоты, поскольку арифметически верный, но при этом понятный процессору способ занял бы слишком много места.

zloelamo ★★★★
()
Ответ на: комментарий от zloelamo

нет, не так.
у меня есть продукт, который говорит, что умеет считать.
я ему говорю «сложи 2+3». он складывает, не выдает ошибок, предупреждений и прочего. ответ - 4. ...
библиотеки для вычислений созданы не для каких-то там вычислений, а именно для арифметических, если не указано другое.

anonymous_sapiens ★★★★★
()
Ответ на: комментарий от anonymous_sapiens

Тогда так: у тебя есть продукт, который умеет считать, при этом оперируя действительными числами в формате IEEE 754. Этот стандарт накладывает определенные ограничения, не ознакомившись с которыми ты не вправе требовать получения какого-то нибыло результата, отличного от получаемого. Это фича, и тебе о ней просто не рассказали в свое время.

azure ★★
()
Ответ на: комментарий от anonymous_sapiens

у меня есть продукт, который говорит, что умеет считать.

Кто тебе сказал, что С умеет арифметически правильно складывать float переменные? Покажи это место в стандарте.

Считать он умеет, безусловно, но это не арфиметический счет, а несколько другой, изучаемый другими разделами математики. В любой нормальной документации по С тебе скажут, что арифметически точные вычисления с типом float невозможны.

я ему говорю «сложи 2+3»

Пример не удачен, поскольку именно тут 5 будет без вопросов, а вот с float все тонко.

библиотеки для вычислений созданы не для каких-то там вычислений, а именно для арифметических, если не указано другое

Во-первых, кто тебе мешает пользоваться этими библиотеками? ТС использовал низкоуровневую операцию процессора, а не вызов библиотечной функции.

Во-вторых, никто тебе не обещал арифметику, ты просто сам в это поверил на основании школьного опыта.

zloelamo ★★★★
()
Ответ на: комментарий от anonymous_sapiens

нет, если по стандарту, то я вопросов не имею.

т.е. это нормально - ни на секунду не задумавшись о том, сколько людей и какой квалификации писали стандарт/компилятор/программы на данном языке, ляпнуть

Ха, все именно так, как получилось, а не так как должно быть.
Должен быть правильный ответ, а не оправдания, почему это не так.

а потом, пытаясь сотворить хорошую мину при плохой игре, снисходительно вякнуть «я вопросов не имею»?

Выключай «анацефал-мод», а то страну жалко или проследуй в биорекатор

yyk ★★★★★
()

Зато у double погрешность поменьше:

12.01.19 16:15 /tmp
cat > 1.c
#include <stdio.h>
int main(){
double f = 0.;
int i;
for (i = 0; i < 100000; i++) f += 0.1;
printf("f = %g\n", f);
}
12.01.19 16:17 /tmp
gcc 1.c && ./a.out
f = 10000

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от anonymous_sapiens

простите, что вы сказали?

у тебя с глазами проблемы? или дислексия?

говорю - в пень иди

yyk ★★★★★
()
Ответ на: комментарий от yyk

а потом, пытаясь сотворить хорошую мину при плохой игре, снисходительно вякнуть «я вопросов не имею»?

Это называется «признать свою ошибку». Такое бывает, представь себе.

stack_protector
()
Ответ на: комментарий от stack_protector

Это называется «признать свою ошибку».

Можете это хоть минетом называть - ума всё равно не добавит

yyk ★★★★★
()
Ответ на: комментарий от anonymous_sapiens

вам нелегко придется в жизни. ничего не понятно же

Тебе не понятно, чему я не удивлён, а ты мне проблемы пророчишь? У тебя ещё и с логикой совсем плохо...

yyk ★★★★★
()

зачем тебе float? он нужен только в видяхах не умеющих двойную точность...

olegsov
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.