W&PBBS - Проблема із заокругленням у Delphi

Народ, допоможіть, плиз!

procedure TForm1.Button1Click(Sender: TObject); Var des1: integer; p,r: real; begin r := 30.45; p := frac(r)*60; des1 := round(int(p)); MessageDLG(inttostr(des1),mtInformation,[mbOk],0); end;

У мене ця програма видає повідомлення з текстом "26". Якщо не помиляюся, то 0,45*60=27. За ідеєю, round(int(27))=27. Чи мене глючить? Делфі в мене сьома.

Scandinavian: Коли ти робиш int(p) значення p обрізається до int без округлення, просто відкидається дрібна частина. Тобто до округлення там було щось на кшталт 26.99999999999999 після int(p) вийшло 26, відповідно round(26) повертає 26.

Scandinavian пише: Якщо я не помиляюся, то 0,45 * 60 = 27 Тут ти помиляєшся. Справа в тому, що числа з точкою, що плаває, видаються з обмеженою точністю. Наприклад, під тип Extended відведено 10 байт. За допомогою 10 байт можна уявити 256^10 різних чисел, тобто близько 10^24. Однак максимальне значення для Extended судячи з хелпу 1.18 x 10^4932. Як бачимо, це набагато більше. Тому не будь-яке число можна уявити з абсолютною точністю. Відповідно 0.45*60.0 не обов'язково дорівнюватиме 27.0.

Я трохи додав у попередній пост, поки редагував з'явилася твоя відповідь.

А що саме видається нелогічним? І навіщо ти перед округленням відокремлюєш цілу частину числа, адже округлення і так залишить цілу частину?

Scandinavian: Ось давай розберемося з твоїм кодом. Для початку наведемо опис стандартних функцій Делфі:

функція Int(X: Extended): Extended; функція Round(X: Extended): Int64; функція Trunc(X: Extended): Int64;

Звідси ми бачимо чому тобі довелося використати нагромадження

Усі абсолютно правильно помітили,що воно явно надмірне. Але якщо ми приберемо функцію round, то отримаємо несумісність типів змінної des1 і результату виразу праворуч оператора присвоювання. Тобто. Integer = Extended. Звичайно Делфі з цим ні за що не погодиться, тому і з'явилася функція round, яка в даному випадку просто забезпечує явне перетворення типів. Насправді все дуже легко дозволяється з використанням функції Trunc. Вона як і Int просто відкидає дрібну частину, але результат при цьому буде цілого типу, що тобі і потрібно в даному випадку. Отже, має бути так:

І має рацію Paul, коли говорить про помилки представлення чисел у ПК, зумовлених кінцівкою розрядної сітки та деякими іншими факторами. Це не нонсенс, це цілком реально, коли 2,0 * 2,0 = 3,9999999999999999999999999999. Ну може трохи перебільшено :D І в нашому випадку на жаль 0,45 * 60 = 26.999999999999957 а не 27,00 як слід зі звичайної арифметики. Але як прозорливо зауважив Зорро, в даному випадку ця проблема "лікується" використанням більш потужного типу - Extended. У результаті приходимо до наступного коду:

procedure TForm1.Button1Click(Sender: TObject); Var des1: integer; p,r: Extended; begin r := 30.45; p := frac(r)*60; des1: = Trunc (p); MessageDLG(inttostr(des1),mtInformation,[mbOk],0); end;

Дякуємо всім за допомогу! Я завжди думала чомусь, що в "комп'ютері" достатньо сили, щоб правильно порахувати. Тепер ситуація прояснилася:). Єдино, моя проблема не зовсім вирішилася, т.к. той код, який я привела у першому повідомленні, відрізняється від справжнього, де змінна r береться з бд через ADO. Я змінила у процедурі все на extended.

procedure Test(r: extended); Var des1: integer; p: Extended; begin p:= frac(r)*60; des1: = Trunc (p); MessageDLG(inttostr(des1),mtInformation,[mbOk],0); end; Test(RemoteADOQuery.Fields[LATITUDE].AsFloat);

При LATITUDE=30.45 отримую 26. Чи може залежати від типу поля 'LATITUDE' у бд? Заздалегідь дякую всім!

Paul, дякую!

Ось так ось дружище Зорро :D :D :D