Ceil непонятная обработка выражения

Вопросы программирования и использования среды Lazarus.

Модератор: Модераторы

Ceil непонятная обработка выражения

Сообщение resident » 11.09.2013 21:28:55

Здрасть :)
Отдельные значения Ceil обрабатывает нормально. Но с выражениями - дробями переменных считает по разному. Подскажите в чем дело?

Пример:
Ceil((A - B)/C)

Код: Выделить всё
unit Unit1;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ExtCtrls,
  StdCtrls, Math;

type

  { TfmMain }

  TfmMain = class(TForm)
    btCalculate: TButton;
    leA: TLabeledEdit;
    leB: TLabeledEdit;
    leC: TLabeledEdit;
    leD: TLabeledEdit;
    leE: TLabeledEdit;
    leF: TLabeledEdit;
    procedure btCalculateClick(Sender: TObject);
  private
    { private declarations }
  public
    { public declarations }
  end;

var
  fmMain: TfmMain;

implementation

{$R *.lfm}

{ TfmMain }

procedure TfmMain.btCalculateClick(Sender: TObject);
  var
    A, B, C, D, E, F: real;
  begin
    A := StrToFloat(leA.Text);
    B := StrToFloat(leB.Text);
    C := StrToFloat(leC.Text);
    D := (A - B)/C;
    E := Ceil((A - B)/C);
    F := Ceil(D);
    leD.Text := FloatToStr(D);
    leE.Text := FloatToStr(E);
    leF.Text := FloatToStr(F);
  end;

end. 

Понятней наверное картинками:
1) С целыми числами - всё Ок
2) Уменьшаю в 10 раз - всё Ок
3) Уменьшаю еще в 10 раз - получаю отличие, значение ошибочно увеличивается на единицу
Изображение

Добавлено спустя 21 минуту 55 секунд:
Ухахочешься, уменьшил значения еще в 10 раз. Опять верный ответ (4).
Изображение
Так что получается, что при каких-то значениях Ceil неправильно обработал выражение.
Что же теперь делать, свою функцию чтоль писать? Но на основе чего? Этих же глюченных функций :)

Добавлено спустя 7 минут 8 секунд:
Вот проект
http://listingimg.s3.amazonaws.com/test.zip
resident
энтузиаст
 
Сообщения: 605
Зарегистрирован: 13.03.2013 16:58:51

Re: Ceil непонятная обработка выражения

Сообщение zub » 11.09.2013 22:49:57

>>Что же теперь делать, свою функцию чтоль писать? Но на основе чего? Этих же глюченных функций
Что делать?, всё пропало!!111))
Если нечего делать, то нужно почитать чтонибудь умное. например про особенности операций с плавающей точкой.
В краце суть примерно такая:
Для флоатов и прочих дублей и екстендовт в результате операций над ними никогда не будет ровного значени например 4 как вы подразумеваете.
будет 3.999много9 или 4.0000много01 т.е. очень близко, но неровно.
и это всегда нужно учитывать, способы описаны в множестве статей любезно находимых гуглем

в вашем случае всё верно, Ceil как и задумано округлил 4.0много01 к ближайшему большему, т.е. =5

Добавлено спустя 5 минут 21 секунду:
>>над ними никогда не будет ровного значени например
тут я конечно вру, бывает, но ооочень редко и на это не нужно расчитывать
zub
долгожитель
 
Сообщения: 2887
Зарегистрирован: 14.11.2005 23:51:26

Re: Ceil непонятная обработка выражения

Сообщение resident » 11.09.2013 23:37:21

Нда, точно, вставил еще такую строчку
Код: Выделить всё
if Frac((A - B)/C) > 0 then ShowMessage('Шеф! Всё пропало!');

И выяснилось, что сюрпризы даже там, где вроде и сейчас правильно считает.
Так что делать? :)
Мне точность не нужна. Там сотые максимум играют роль.
А если округлять дробь, например, до десятитысячных и потом уже подавать её в функцию Ceil?
resident
энтузиаст
 
Сообщения: 605
Зарегистрирован: 13.03.2013 16:58:51

Re: Ceil непонятная обработка выражения

Сообщение zub » 12.09.2013 00:14:08

ввести в программу еще переменную, задающую допустимую погрешность, например:
Код: Выделить всё
const
  delta=e-10;

в случае если таки важен ceil делать так:
Код: Выделить всё
E := Ceil((A - B)/C-delta);
F := Ceil(D-delta);


погуглил за тебя, спасибо можешь не говорить:
http://www.delphikingdom.com/asp/viewit ... alogid=374
zub
долгожитель
 
Сообщения: 2887
Зарегистрирован: 14.11.2005 23:51:26

Re: Ceil непонятная обработка выражения

Сообщение resident » 12.09.2013 00:43:46

zub писал(а):в случае если таки важен ceil делать так

А вот если значение дроби (A - B)/C будет равно = 4.0много01 + delta
Тогда значение в Ceil-е будет опять же равно 4.0много01 и опять будет та же ошибка.
Поэтому я и спросил про функцию округления, может будет вернее округлять до тех же e-10? А потом уже округленное значение подсовывать в Ceil.

zub писал(а):погуглил за тебя, спасибо можешь не говорить

О Боже, как давно это было. "Сейчас во Флориде и в Королевстве" эпоха застоя.
Ок, тогда пиши Paypal адрес :)
resident
энтузиаст
 
Сообщения: 605
Зарегистрирован: 13.03.2013 16:58:51

Re: Ceil непонятная обработка выражения

Сообщение zub » 12.09.2013 01:05:51

>>А вот если значение дроби (A - B)/C будет равно = 4.0много01 + delta
>>Тогда значение в Ceil-е будет опять же равно 4.0много01 и опять будет та же ошибка.
А откуда я знаю что в конечном итоге нужно получить? гугль знает, я нет)) Природа "ошибки" понятна? - Шеф! не всё пропало!
это общий принцип работы с вещественными числами, всегда давать некоторую разбежку, покрывающую "погрешность" проца, но укладывающуюся в точность задачи

>>4.0много01 + delta
ну дак это число больше 4 на значение большее чем допустимая нами погрешность, соответственно его и цилкать до пяти надо, а никак не до четырех.
zub
долгожитель
 
Сообщения: 2887
Зарегистрирован: 14.11.2005 23:51:26

Re: Ceil непонятная обработка выражения

Сообщение alexey38 » 12.09.2013 09:03:12

resident писал(а):Поэтому я и спросил про функцию округления

Посмотрите на Round или RoundTo, ибо Ceil округляет число в сторону увеличения, а round до ближайшего целого..
alexey38
долгожитель
 
Сообщения: 1627
Зарегистрирован: 27.04.2011 19:42:31

Re: Ceil непонятная обработка выражения

Сообщение Alex2013 » 12.09.2013 13:38:32

Если нужно ПОКАЗАТЬ сугубо до сотых то вроде Str (FolatNum:1:2,S ) никто не отменял ... :idea:
Alex2013
долгожитель
 
Сообщения: 3146
Зарегистрирован: 03.04.2013 11:59:44

Re: Ceil непонятная обработка выражения

Сообщение resident » 12.09.2013 21:15:25

zub писал(а):это общий принцип работы с вещественными числами, всегда давать некоторую разбежку, покрывающую "погрешность" проца, но укладывающуюся в точность задачи

Понятно.
Провожу округление до нужной точности и ответ подаю в Ceil, вроде работает.

zub писал(а):ну дак это число больше 4 на значение большее чем допустимая нами погрешность, соответственно его и цилкать до пяти надо, а никак не до четырех.

Да, точно. У меня уже мозги заклинило вчера.

zub писал(а):Природа "ошибки" понятна? - Шеф! не всё пропало!

Да, спасиб :)
resident
энтузиаст
 
Сообщения: 605
Зарегистрирован: 13.03.2013 16:58:51

Re: Ceil непонятная обработка выражения

Сообщение alexey38 » 13.09.2013 04:35:55

resident писал(а):Провожу округление до нужной точности и ответ подаю в Ceil, вроде работает.

Все таки я Вам предлагаю не использовать Ceil (я эту функцию в своей практике например никогда не использовал), а посмотреть в сторону Round или RoundTo.

1.function Round ( const Number : Extended ) : Int64;
Функция Round округляет число с плавающей запятой (Number) до целого значения.

2. function Trunc ( const Number : Extended ) : Integer;
Функция Trunc возвращает целочисленную часть числа с плавающей запятой.

3. Функция Ceil( X: Extended ): Integer;
Функция округляет значение аргумента X к ближайшему боль-шему целому. Абсолютное значение X не должно превышать величину MaxInt ( 2 147 483 647 ).

4. Функция Floor( X: Extended ): Integer;
Функция округляет значение аргумента X к ближайшему меньшему целому. Абсолютное значение X не должно превышать величину MaxInt ( 2 147 483 647 ).
alexey38
долгожитель
 
Сообщения: 1627
Зарегистрирован: 27.04.2011 19:42:31

Re: Ceil непонятная обработка выражения

Сообщение debi12345 » 13.09.2013 09:37:46

С CURRENCY-типом все четко:
Код: Выделить всё
program ceiltest;

uses sysutils, math;

var
  A,B,C,D,E,F: currency;
begin
  A:= 10;
  B:= 2;
  C:= 2;
  D := (A - B)/C;
  E := Ceil((A - B)/C);
  F := Ceil(D);
  writeln('1:1-> (A - B)/C',D);
  writeln('1:1-> Ceil((A - B)/C',E);
  writeln('1:1-> Ceil(D)',F);

  A:= 1;
  B:= 0.2;
  C:= 0.2;
  D := (A - B)/C;
  E := Ceil((A - B)/C);
  F := Ceil(D);
  writeln('1:10-> (A - B)/C',D);
  writeln('1:10-> Ceil((A - B)/C',E);
  writeln('1:10-> Ceil(D)',F);

  A:= 0.1;
  B:= 0.02;
  C:= 0.02;
  D := (A - B)/C;
  E := Ceil((A - B)/C);
  F := Ceil(D);
  writeln('1:100-> (A - B)/C',D);
  writeln('1:100-> Ceil((A - B)/C',E);
  writeln('1:100-> Ceil(D)',F);

end.

1:1-> (A - B)/C 4.000000000000000000E+00
1:1-> Ceil((A - B)/C 4.000000000000000000E+00
1:1-> Ceil(D) 4.000000000000000000E+00
1:10-> (A - B)/C 4.000000000000000000E+00
1:10-> Ceil((A - B)/C 4.000000000000000000E+00
1:10-> Ceil(D) 4.000000000000000000E+00
1:100-> (A - B)/C 4.000000000000000000E+00
1:100-> Ceil((A - B)/C 4.000000000000000000E+00
1:100-> Ceil(D) 4.000000000000000000E+00

вопрос в допустимом диапазоне значений для этого типа...

Добавлено спустя 15 минут 28 секунд:
В данном тесте диапазон значений CURRENCY ограничен :
+-920,000,000,000,000.999999..(конца и края не видно - поэтому и нет ошибок округления);

то есть 921 квинтиллион. Так что надо смотреть реально используемый оный в вашей задаче

Добавлено спустя 8 минут 5 секунд:
Хм,.. Используя гигантское допустимое количестов знаков после запятой у типа CURRENCY, в нем можно хранить(предварительно деля) гораздо большие числа, у которых после точки мало знаков. Надо взять на заметку :)

Добавлено спустя 25 минут 6 секунд:
Особенно прикололи правильные значение после очень lossy-преобразований со степенями
Код: Выделить всё
 
  A:= power(power(0.001,1/909),909);
  B:= power(power(0.0002,1/113),113);
  C:= power(power(0.0002,1/2),2);
/code]
или чтобы исключить оптимизатор :
[code]
  A:= power(0.001,1/909;
  A:= power(A,909;

  B:= power(0.0002,1/113);
  B:= power(B,113);

  C:= power(0.0002,1/2);
  C:= power(C,2);


одно "но" - больше знаков после запятой в этом случае использовать нельзя - иначе числа конвертятся в ноль. Познавательно, однако :)
Аватара пользователя
debi12345
долгожитель
 
Сообщения: 5761
Зарегистрирован: 10.05.2006 23:41:15
Откуда: Ташкент (Узбекистан)


Вернуться в Lazarus

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 221

Рейтинг@Mail.ru