Закръгляване float( C/C++ )

26 отговора 1086 прегледа
#1

Закръгляване float( C/C++ )

Колко мислите, че се получава при израза
int I = 0.325f * 1000;
?
Правилнооооо, 324. Точно така. Сещаме се защо. Някой има ли идея как да напиша свестна функция, от рода на
Round ( float X, int Precision ) ,
която закръглява такива числа на 0.33, а не 0.32 при извикване Round(0.325f, 2 );
Изглежд доста елементарно като го погледнеш, ама ми пийна сосчето. Накрая написах нещо от рода на +0.500001 вместо + 0.5 и ми покриваше тестовите примери, ама едва ли това е решението.

Контрольорите приличат на Батман.
Отговор
в отговор на Colombino #2

Re: Закръгляване float( C/C++ )

> Колко мислите, че се получава при израза
int I = 0.325f * 1000;
?
Правилнооооо, 324. Точно така. Сещаме се защо.

Това, за което аз се сещам е, че ти е счупен компилаторът Има ли нещо друго, за което не се сещам?

Отговор
в отговор на hoopla #3

Re: Закръгляване float( C/C++ )

> Това, за което аз се сещам е, че ти е счупен компилаторът
Ако го пробваш с gcc ще бъдеш изненадан. Поне аз бях. Първо го написах както го е дал Colombino и сложиш един printf на I, резултата си беше 325. Да ама като го видях с gdb-то се оказа че gcc-то си го е оптимизирало (без да съм му дал никакви опции за оптимизиране) и директно присвоява 325 на I. Затова му написах една функция


int qwerty (float f)
{
return (f * 1000);
}


I = qwerty (0.325);
printf ("I = %d\n", I);

И резултата е 324.

Отговор
в отговор на Penguin #4

Re: Закръгляване float( C/C++ )

Така е. Като го изолираш във функция, и gcc дава 324. Оптимизиран с -O3 обаче, пак се изхитрява да даде 325.

И Kylix 3 ми даде 325 в първия вариант. Значи s/счупен/глупав/

Отговор
в отговор на hoopla #5

Re: Закръгляване float( C/C++ )

> Оптимизиран с -O3 обаче, пак се изхитрява да даде 325.
Аха. Всъщност сега като го видях с gdb-то при оптимизация въобще не вика помощната функция, а директно си записва 325 в променливата. Значи фирмата на Colombinо ще спести доста от бета тестване, щото щат не щат ще трябва да прескочат дебъгване и бета тестване и директно да пуснат release версия със всички оптимизации :-)

Отговор
в отговор на Penguin #6

Re: Закръгляване float( C/C++ )

Аз когато закръглявам обикновено пиша нещо от рода на

float x = 0.325f
int i = (int)floor(x*100 + 0.5);

Не съм се замислял до сега, и винаги съм вярвал че трябва да работи: нали 0.325 * 100 + 0.5 = 32.5 + 0.5 = 33.0 и floor(...) трябва да ни даде 33.0 а не нещо друго. Обаче като го компилирах с VC++6.0 даде резултат 32, против всякаква логика.

Не зная дали се е счупил компилатора, но може би е просто естествена и допустима грешка при смятането със плаваща точка - когато опитах с x = 0.425 ми даде правилен резултат 43 (a za x = 515 otnovo greshi). Обаче дветe числа както си приличат в 10-тична бройна система ... бая се различават в 2-ична дето се ползва в стандарта на IEEE Floating Point.

Ако някой има време да развие тези дробни числа в 2-ична бройна система и да извърши в нея на ръка алгебричните операции, сигурно ще разбере къде е проблема - в компилатора, съдбата, или че ние мислим погрешно.

Само предполагам, не ми е известен отговора.
В детайли стандарта на IEEE е много гаден - числата floating point не нарастват с константна стъпка и блиzко около 0-лата са направили правилото по-различно за да икономисва памет давайки по-голяма точност. Около максимума \infinity пак има гадости. Така че никой да не предполага математическите му предположения да са 100% вярни в аритметиката на floating point.

Отговор
в отговор на Colombino #7

Re: Закръгляване float( C/C++ )

Мислех, че всеки се сеща какъв е проблема. Очевидно представянето на 0.325 във float е число малко по-малко от 0.325. Като се умножи по 1000f се получава по-малко от кръглото число и като се отреже - шат пада цяла единица от числото.
Все пак някой да има по свестна идея как се закръгляват такива неща? Щото това дето съм го написал ми изглежда съмнително.

Контрольорите приличат на Батман.
Отговор
в отговор на Colombino #8

Re: Закръгляване float( C/C++ )

v koprocesorta cisloto 0.325 se predstavia kato:
0.32499998807907104

koeto ocevidno ste bade otriazano kato 0.324

kompilatora nema nisto obsto s tova! tova si go varsi koprocesor aosven tova e "viarno"

prosto trebva ne da rejes a da zakravliavas!
drug e varposa ce naistina plavastata aritmetika sazdava dosia iadove kogato trebva da preobrazuvas kam celo cislo.....

Отговор
в отговор на Colombino #9

Re: Закръгляване float( C/C++ )

smiatai s double :)
po princip mai float imashe okolo 7 tochni desteichni znaka...
ama moje i da sa 6 ne sym izdrebniawal tolkowa che da gi smiatam
a rabotia s double. tam mai sa 16 znacite na koito moje da razchitash
inache, prontf na VC i gcc zakrygliawat razlichno kogato stawa
duma za zakrygliwane na cifra sledwashtata sled koiato e 5
zashtoto ima i coprocesora ima rejim na zakrygliawane pri koito
ako e 5 ne se zakrygliawa nagore ako cifrata e chetna t.e. winagi
ostawa chetno.
do kolkoto razbrah ideieata bila poneje w finansowite smetki
chesto se zakrygliawalo s 5 i taka se namaliawala greshkata
a microsofskia printf zakrygliawa pri '5' nagore

FPU ima control register
w koito :
Bits 10 & 11 :
00 to nearest or even
01 round down
10 round up
11 truncate
i FRNDIT zakrygliawa w zawisimost ot control registera
The "00" setting is the default. The 80x87 rounds values above one-half of the least significant bit up. It rounds values below one-half of the least significant bit down. If the value below the least significant bit is exactly one-half the least significant bit, the 80x87 rounds the value towards the value whose least significant bit is zero. For long strings of computations, this provides a reasonable, automatic, way to maintain maximum precision.

Отговор
в отговор на emo #10

Re: Закръгляване float( C/C++ )

propusnah li da spomena che pri priswoiawane ne se zakrygliawa a se otriazwa
drobnata chast....

Отговор
в отговор на Colombino #11

Re: Закръгляване float( C/C++ )

> Мислех, че всеки се сеща какъв е проблема.

Добре де, всички знаем какво е грешка от закръгление. Просто по начина, по който го беше написал, и двата компилатора, с които пробвах, го оптимизираха и го представиха правилно. Опитах се да подкарам пак и на Intel icc, ама съм забравил къде да му туря лицензния файл, та не стана.

qwe вече ти даде доста добър начин.

Има и друг - фиксирана запетая. Супер производителност и точност, но ограничена област на употреба. Става за числа, които се намират в относително тесен диапазон. Значи хващаш long, или long long, и си ги представяш като цяла и дробна част, но с имагинерна десетична точка, която не се мести.

Отговор
в отговор на Colombino #12

Re: Закръгляване float( C/C++ )

az go probvah s Turbo C V2.01 1987 i si beshe 325

i ne bi li bilo po korektno da napishesh taka:
int I = (int) 0.325f * 1000;
znam li ?


Отговор
в отговор на Colombino #13

Re: Закръгляване float( C/C++ )

Знаете, че не винаги човек може да си избира с какво да работи. Трябваше да напиша функция

float Round(float, int);

Винаги ми е правило лошо впечатление, когато седнат да ми обясняват как се решава друг проблем, а не моя. Аз никак не съм начинаеш за да се нуждая от съвет с какви данни да работя. Това за присвояването на int беше само за илюстрация на проблема.
Опитах вътрешно да премина през double, ама нямаше голяма файда, щото то числото вече е станало по-малко. Очевидно никой не знае по какъв начин да извърша закръгляването за да съм максимално близо до истината.

Контрольорите приличат на Батман.
Отговор
в отговор на Colombino #14

Re: Закръгляване float( C/C++ )

ami az sym ti napisal kak FPU zakrygliawa
pyk ti si prawi izwodite, ako iskash pishi na asm
tam tochnosta se zadawa w bitowe

Отговор
в отговор на Colombino #15

Re: Закръгляване float( C/C++ )

E, TOCNO TAKA DE!
trebva da si napises ROUND
tova e nacina! nema drug!

kato znaes sto pitas?

Отговор
в отговор на emo #16

Re: Закръгляване float( C/C++ )

zadava se, ama nisto ne se zadava!
onaia tocnost nema da ti svarsi rabota!
nito flagovete deto opredeliat kak da se zakraglia ste resat problema!
ste go resat v konkretina slucai, no v drig ste gramne!
t.e. kogato preobrazuvas plavasta zapetaia v celo! vinagi moje da si nepriatno iznenadan!

Отговор
в отговор на Colombino #17

Що не пробваш с double ?

Рядко боравя с дробни числа и там основния проблем ми е равно ли е числото на нула, ама той е елементарен ...

За да те уважават е нужно самоуважение!
Отговор
в отговор на ЯPOCTcлaв #18

Re: Що не пробваш с double ?

elementaren dryjki za -0 da si chuval :-)

Отговор
в отговор на CALL-151 #19

Re: Закръгляване float( C/C++ )

Ще си напише round(), ама другия път.

Защо в стандарта на ANSI C не са дефинирали такава библиотечна функция? Дефинирали са ceil() и floor(), а не са дефинирали round(). Сигурно добре са знаели какво правят.

В този пример, 0.325 = 325/1000 = 13/40 няма как да се представи в краен вид чрез 2-ична система 13/40 = b1/2 + b2/4 + b3/8 + ... + bn/(2^-n) + ... Не може, защото знаменателя 40 не дели никоя степен на 2. Както не можеш в 10-тична система да запишеш 1/3 = 0.3333... в краен вид, понеже 3 не дели никоя степен на 10.

Още като се опиташ да запишеш такова (безкрайно) число в паметта, която е крайна, всякаква точност отива по дяволите. После компютъра от де ще знае че си искал да запишеш именно 0.325 а не 0.324999...; няма как да знае, защото за него точното число 0.325 просто не съществува. Закръгляването чрез добавяне на 0.5 = 1/2 е съвсем правилно от гледна точка на 2-ична бройна система.

int i = (int)(x * 100.0 + 0.5); // правилно

Всяко друго хитруване като "+ 0.50001" води до по-грешен резултат. На който не му харесва да не ползва floating point.

Отговор
в отговор на qwe #20

Re: Закръгляване float( C/C++ )

Не съм убеден, че наистина резулатът е по-грешен. Тук се явява и малко статистика. Просто е по-вероятно едно число да е 0.325, а не 0.324999.., ако е взето от живота, а не е абсолютно случайно. Аз знам, че никога не мога да имам пълна точност. Просто искам грешката ми да се проявява в минимално количество случаи. Даже сериозно ме съблазни идеята за sprintf("%g", ...) и после scanf . Въпросът е как се прави подобно нещо, а не винаги ли ще е точно.

Контрольорите приличат на Батман.
Отговор