Rambler's Top100
"Knowledge itself is power"
F.Bacon
Поиск | Карта сайта | Помощь | О проекте | ТТХ  
 Hello, World!
  
 

Фильтр по датам

 
 К н и г и
 
Книжная полка
 
 
Библиотека
 
  
  
 


Поиск
 
Поиск по КС
Поиск в статьях
Яndex© + Google©
Поиск книг

 
  
Тематический каталог
Все манускрипты

 
  
Карта VCL
ОШИБКИ
Сообщения системы

 
Форумы
 
Круглый стол
Новые вопросы

 
  
Базарная площадь
Городская площадь

 
   
С Л С

 
Летопись
 
Королевские Хроники
Рыцарский Зал
Глас народа!

 
  
ТТХ
Конкурсы
Королевская клюква

 
Разделы
 
Hello, World!
Лицей

Квинтана

 
  
Сокровищница
Подземелье Магов
Подводные камни
Свитки

 
  
Школа ОБЕРОНА

 
  
Арсенальная башня
Фолианты
Полигон

 
  
Книга Песка
Дальние земли

 
  
АРХИВЫ

 
 

Сейчас на сайте присутствуют:
 
  
 
Во Флориде и в Королевстве сейчас  03:38[Войти] | [Зарегистрироваться]

Обсуждение материала
Неочевидные особенности вещественных чисел
Полный текст материала


Другие публикации автора: Антон Григорьев

Цитата или краткий комментарий:

«... Статья ориентирована на начинающих, но содержит некоторые нетривиальные сведения; в частности, почему в системах Windows 9x вычисления с типом Extended производятся с точностью типа Double
'Когда-то описание внутреннего представления таких чисел было неотъемлемой частью любой сколь-нибудь серьёзной книги по программированию, но сейчас у авторов появились более интересные предметы для обсуждения: COM/DCOM, ActiveX, OLE и многое другое. На вещественные числа просто не хватает места. И люди, начавшие программирование с Delphi и не имеющие опыта работы в более старых средах, часто оказываются совершенно беспомощными перед непонятным поведением программы, содержащей дробные вычисления ...'
...»


Важно:
  • Страница предназначена для обсуждения материала, его содержания, полезности, соответствия действительности и так далее. Смысл не в разборке, а в приближении к истине :о) и пользе для всех.
  • Любые другие сообщения или вопросы, а так же личные эмоции в адрес авторов и полемика, не относящаяся к теме обсуждаемого материала, будут удаляться без предупреждения авторов, дабы не мешать жителям нормально общаться.
  • При голосовании учитывайте уровень, на который расчитан материал. "Интересность и полезность" имеет смысл оценивать относительно того, кому именно предназначался материал.
  • Размер одного сообщений не должен превышать 5К. Если Вам нужно сказать больше, сделайте это за два раза. Или, что в данной ситуации правильнее, напишите свою статью.
Всегда легче осудить сделанное, нежели сделать самому. Поэтому, пожалуйста, соблюдайте правила Королевства и уважайте друг друга.



Добавить свое мнение.

Результаты голосования
Оценка содержания

  Содержит полезные и(или) интересные сведения
[1]5898.3%
 
  Ничего особенно нового и интересного
[2]00%
 
  Написано неверно (обязательно укажите почему)
[3]11.7%
 
Всего проголосовали: 59

Оценка стиля изложения

  Все понятно, материал читается легко
[1]4291.3%
 
  Есть неясности в изложении
[2]48.7%
 
  Непонятно написано, трудно читается
[3]00%
 
Всего проголосовали: 46




Смотрите также материалы по темам:
[Вещественные числа]

Комментарии жителей
Отслеживать это обсуждение

Всего сообщений: 77

01-07-2019 03:46
Во-первых, спасибо автору за познавательную и толково написанную статью. Во-вторых, наткнулся на одну ошибку в тексте. Считаю полезным привести подробности по этой теме для других читателей. В статье сказано, что если поле PC (биты 8 и 9) регистра CWR указывают на то, что вычисления надо производить в четырёх- или восьмибайтовом форматах (форматах Singlе или Double), то по сравнению с Extended возрастает скорость в ущерб точности. Это не так. Преимущества в скорости не будет. Дело в том, что для вычислений различных математических функций используются жёстко предопределённые внутренние алгоритмы, поэтому количество требуемых вычислительных затрат от изменения разрядности представления чисел не зависит. Такая возможность сделана в большей степени для нужд отладки - чтобы программист мог получить в точности тот же результат, какой он получил бы, если бы для хранения промежуточных результатов вычислений вместо регистров сопроцессора использовал переменные в памяти типов Single или Double. Преимущество этих типов перед Extended достигается только за счёт экономии на пересылке данных в оперативной памяти. Более ощутимое преимущество в скорости от использования типов Single или Double можно получить, если вместо команд FPU использовать команды из наборов SSE. Правда, там есть только простейшие арифметические операции.


31-05-2010 20:55
Есть ещё подводные камни с этими "противными" вещественными числами
Например, "от перемены мест слагаемых сумма изменяется"
а виновато выравнивание порядков


Да, таких мест может быть много. На всякий случай поясню ваш пример. Вещественное число хранит фиксированное число разрядов, а экспонента, по сути дела, указывает, в каком диапазоне лежат эти разряды. Т.е., например, 23 бита мантиссы могут хранить 23 двоичных разряда в диапазоне от 2^10 до 2^-42, а могут - от 2^-80 до 2^-132 - это зависит от значения экспоненты. Другими словами, в большом диапазоне возможных значений есть относительно небольшое "окно", которое может перемещаться по этому диапазону. Верхняя граница этого окна выравнивается на старший разряд. Соответственно, младшие разряды, отстоящие слишком далеко от старшего, не попадают в окно и отбрасываются.

В первом варианте мы изначально работаем с маленькими числами, поэтому окно выровнено так, чтобы вмещать в себя их с достаточной точностью. За счёт многократных сложений число увеличивается на 6 десятичных порядков (около 20 двоичных), но так как каждый раз прибавляется маленькое число, окно перемещается постепенно, и существенной потери точности не происходит. И когда мы прибавляем сумму к большому числу, они оба умещаются в одном окне.

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


Есть и более простое объяснение:
в типе Single под мантиссу отведено 23 бита, то есть там может быть 7-значное число,
а при операции 1000000+0.000001=1000000.000001 получается 13-значное, младшие разряды отсекаются

Вообще (ИМХО) выход из всей этой белиберды - это BCD числа, с их помощью можно получить просто умопомрачительную точность, какая Extended'у и не снилась в самой смелой фантазии. Другое дело конешно скорость таких вычислений.


21-06-2009 10:16
Очень интересная статья, спасибо.
На прочтение (повторное) сподвигнула проблема с RoundTo. Когда имеем значение Double как 1,4676311039e+300, и точность +4.-4, то RoundTo выкидывает 'Invalid floating point operation'.

Сталкивался ли кто еще с такой проблемой?

Саша



11-06-2009 01:15
Интересная статья. Но как-то не натыкался на ошибки, связанные с особенностями представления вещественных чисел. Но вот набрел на такую неприятную штуку :)  :

procedure TForm1.Button1Click(Sender: TObject);
var
  x: Integer;
  y: Integer;
  z: Extended;

  XdivYmultZ: Extended;
  res: integer;
begin
  x := 15;
  y := 55;
  z := 55;

  XdivYmultZ := y * ( x / z );  // ставим на этой строке breakpoint, "изменяем" z снова на 55
  res := Trunc( XdivYmultZ );
end;



На строке вычисления XdivYmultZ поставить breakpoint и по Alt+F5 изменить значение "z" опять же на 55 (значение этой переменной для этого примера, по сути, в присваивании можно не задавать) (у меня в программе при вычислениях получалось логически целое значение z=55, а в компьютере где-то терялись младшие разряды).
"XdivYmultZ" будет равно 14.9999999...82. "res", соотвественно, 14, а это уже не логично, тогда как 14.99999... вроде бы и устраивало, и для дальнейших вычислений сошло бы за 15

решил положить этот пример сюда.
 KaCT


24-12-2008 10:38
М-дя... Антон, спасибо за ссылку. Дочитать до конца, правда, не хватило сил, но... Честно говоря, впервые стало стыдно за свой родной институт :( Как-то не приходилось мне пока встречать однокашников, которые вместо знаний демонстрировали бы корочки Физтеха.

Утешает только то, что поскольку автор напирает на то, что он физик, то, скорее всего, он не с Факультета Управления и Прикладной Математики. А значит, наверное, не имел в программе столько курсов по вычислительной математике. Иначе бы знал, что и ракеты летают как надо, и физичесике процессы моделируются как надо. А уж деньги посчитать -- вообще не проблема. И все это нормально рассчитывалось даже на БЭСМ-6. Только нужно считать уметь.

Хотя... Подумалось вот... Нам с певрого курса вбивали, что в физике ничего точно измерить нельзя. Всегда есть какая-то погрешность. Вопрос к физику Юровицкому: А как же мы можем построить, например, мост, если не можем точно посчитать расстояние между двумя берегами? Если Вы ответите на это вопрос, то, наверное, сможете понять, как можно пользоваться компьютерными мчислами с плавающей точкой ддля получения нужного результата. И бабушке не придется отрезать от килограмма колбасы 333.3333333333333333333333333 грамма :D

Впрочем, бабушки безо всякой высшей математики делят батон колбасы на любое наперед заданное количество частей. И довольно точно ;-)
 Geo


24-12-2008 07:37
сообщение от автора материала
Тем, кто заинтересовался сообщением В. Юровицкого, напомню, что я уже приводил ссылку на его "труды" в своём сообщении от 02-05-2007 05:53. Повторяю ссылку: http://elementy.ru/blogs/users/vladyur/10157/ Там интересен не столько текст, сколько комментарии к нему. Особенно ответы Юровицкого. Например, его оппонент вполне корректно разбирает несколько явных ляпов в тексте, а Юровицкий в ответ, вместо контраргументов, говорит следующее:

Юрий, хамство, конечно, запретить трудно. Ваши ухмылочки я как программист со стажем более двадцати лет, выпускник МФТИ, ученик лвуреатов Нобелевской премии Ландау и Капицы я не могу принять. А объяснять вам то, что поймет любой студент, у меня как-то нет желания. И в вашей помощи я, думается, никак не нуждаюсь, так как мне нужны программисты, но несколько более квалифицировапнные.

Личные выпады и размахивания регалиями вместо ответа по существу. И так везде. Не ведитесь на слова Юровицкого, это не тот человек, к чьему мнению следует прислушиваться.


24-12-2008 02:26
Уважаемый Антон Григорьев.
Ваша статья наглядно показывает, что современное компьютерное исчисление вещественных чисел смертельно опасно для нынешней цивилизации, в которой именно компьютер управляет всем - атомными станциями и туалетами.
В чем смысл вашей статьи? Модели, используемые для вычислений на компьютере как правило на множестве действительных чисел. А компьютер их вычисляет на множестве компьютерных. И расхождение может быть сколь угодно велико. Вам стоило бы показать, как из этих "малых" ошибок могут возникать "очень большие". Действительно, стоит разделить одну ошибку на другую и вы получае6те результат абсолютно непредсказуемый. И как вы можете предотвратить такие вещи? Конечно, ВЫ может и смогли бы. Но сейчас програмированием занимаются миллионы людей. И требовать от всех столь высокой грамотности просто нереально.
Таким образом, современный компьютер может давать сколь угодно большие ошибки. И эти вычислительные ошибки практически непредсказуемы. И сколько аварий, катастроф и происшествий связано с вычислительными ошибками мы просто не знаем. Их обычно списывают на "чедовеческий фактор".

Компьютерная технология пришла в опасный тупик. Нужна новая компьютерная вычислительная технология. Об этом см.http://yur.ru/Conference/Cipros/index.htm ·        Текст выступления Юровицкого В.М. Доклад «Общая теория чисел и числовых эпох»


08-12-2008 09:38
Хорошая статья. Если б только на пару лет раньше на глаза попалась...


09-11-2008 10:52
При работе с Vista столкнулся с необходимостью использовать Set8087CW(Get8087CW or $0100)!

Дело в том, что при вычислении расстояния между двумя близкими точками по их GPS-координатам получал 0 на Delphi 7  в то время как на JavaScript правильное значение - 340 метров. Пока не прочитал статью, не мог добиться правильного результата на Delphi...  

Спасибо, уважаемый Антон Григорьев!


30-08-2008 06:35
to Антон Григорьев

Легко видеть, что если бы всё было точно, то A-B дало бы 0.01 и C стало бы равным 0, чего не происходит.
..
Округление обычно нужно только при выводе на экран - ну так и используйте функцию FloatToStrF или Format, а округлять само число в двоичном представлении я смысла не вижу.


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

В приведенном вами примере, для получения корректного результата необходимо опять округлить значение передаваемое в переменную C и все будет именно так как ожидалось.


var
  A, B, C: Extended;
begin
  A := Okrugl(1.111111111, 2);
  B := Okrugl(1.111111111, 1);
  C := Okrugl(100* (A - B) - 1, 4);
  Label1.Caption:=FloatToStr(C);
end;



24-07-2008 03:17
сообщение от автора материала
Andy1618:

А чем вас не устраивает такой абзац, который есть в статье?

Процессор Pentium во всех своих вариантах имел встроенный сопроцессор. Таким образом, с приходом этого процессора тип Real стал как бы обузой, а на передний план вышли Single, Double и Extended. Чтобы свести к минимуму необходимые переделки программ, Borland ввела новую директиву компилятора: {$REALCOMPATIBILITY ON/OFF}. По умолчанию стоит OFF, что означает отсутствие полной совместимости. В этом случае тип Real в Delphi совпадает с типом Double. Если же совместимость включена, тип Real совпадает со своим прообразом из Паскаля. Существует ещё тип Real48, который всегда, вне зависимости от настроек, совпадает со старым Real. Далее в этой статье под словом “Real” я всегда буду подразумевать старый тип. Отмечу, что всё это появилось только в Delphi 4, в более ранних версиях тип Real48 отсутствовал, а тип Real был всегда старым, шестибайтным.


21-07-2008 03:18
Встретился с этим еще работая с бейсиком на ПК "Поиск" пытаясь вывести на экран число 1.0 и сильно удивился, но потом почитав документацию там такая ситуация более менее была объяснена.
Второй раз столкнулся с этим уже на курсах, там были 8086-е и выдавали слегка другой результат, не помню точно но порядок погрешности оставался таким же, но цифры были другие.

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


20-07-2008 04:05
Прелестная статья!
Возможно, стоит где-нибудь сделать сноску, для какой версии Delphi она была написана, поскольку "всё течёт, всё меняется".
К примеру, вот кусочек хелпа про тип Real:
==
The generic type Real, in its current implementation, is equivalent to Double.
...
Note: The six-byte Real48 type was called Real in earlier versions of Object Pascal. If you are recompiling code that uses the older, six-byte Real type, you may want to change it to Real48.
==


06-05-2008 06:31
сообщение от автора материала
Что ж, авторитет Fisher'а по вопросам округления вещественных чисел получил ещё одно подтверждение, не могу не согласиться :)


06-05-2008 06:22
Позволю себе вступиться за смысл существования функций округления.

Светлый идеал использования функций округления в том, что при грамотном их применении можно достичь математически точных результатов (в рамках заданной точности) даже при всех чудесах внутреннего представления чисел.

Другими словами, если предположить, что в приведенном примере А,В и С - числа с двумя знаками после запятой (например, рубли), то можно добиться, что С будет чистым нулем, как и ожидается из школьного курса арифметики, и именно таковым поучаствует в дальнейших вычислениях. Для этого надо не забыть применить функцию Okrugl() не только при вычислении А и В, но и при вычислении С

C := Okrugl(   100*(A - B) - 1  ,2);

Смысл происходящего в том, что хотя вещественное число не может быть равно точно 1.11, но погрешность его представления как 1.11 значительно меньше самого числа 1.11 (для extended - думаю, порядков на 10), и потому погрешность эта в последующих вычислениях при правильном округлении устраняется.

Разумеется, полезность эта может быть получена, только если функции округления не глючны сами по себе, как вредоносная RoundTo. Ну и понятно, что если построить вычисления так, что на каком-то этапе погрешность представления сравняется по порядку с самим числом, то никакое округление не поможет.



06-05-2008 02:00
сообщение от автора материала
Stalker Stalker:

Я не совсем об этом. Вот смотрите, такой код с вашей функцией:

procedure TForm1.Button1Click(Sender: TObject);
var
  A, B, C: Extended;
begin
  A := Okrugl(1.111111111, 2);
  B := Okrugl(1.111111111, 1);
  C := 100* (A - B) - 1;
  Label1.Caption:=FloatToStr(C)
end;


Легко видеть, что если бы всё было точно, то A-B дало бы 0.01 и C стало бы равным 0, чего не происходит. Зная формат хранения вещественного числа, легко понять, что A не может иметь значения 1.11, а B - 1.1, там всё равно будет некоторое округление. Вот я и не понимаю, зачем тратить такие усилия на достижение заведомо неточного результата? Округление обычно нужно только при выводе на экран - ну так и используйте функцию FloatToStrF или Format, а округлять само число в двоичном представлении я смысла не вижу.

P.S. Смысл существования функции RoundTo я, если честно, тоже не понимаю :)


06-05-2008 01:08
К неочевидным особенностям вещестенных чисел хочу приписать свои наблюдения.

Функции Sin(), Cos(), Sqrt() не растут (или убывают) равномерно.
При небольших изменениях аргумента они будут прыгать взад-вперед.
И понятно почему - они вычисляются итерационно при помощи рядов.
Не могу привести список всех таких функций, просто с этими сталкивался сам.

Поскольку в статье это не было упомянуто, я решил, что неплохо об этом тоже сообщить.


04-05-2008 08:02
>>> В результате пришел к выводу, что надежнее всего - "ручной" анализ строчного представления исходного числа.
Правильно! Только не совсем "ручной". В Delphi уже давно есть модуль для работы с числами в таком представлении - FmtBCD.pas. Он рекомендуется для финансовых приложений и т.п.


04-05-2008 06:56
1. Она была написана еще под D3 тогда, никаких RoundTo не было
2. В своей работе пробовал RoundTo, его аналоги и самые разные вариации округления, предлагаемые на разных программерских форумах и всегда были случаи когда результат не совпадал с ожидаемым.

В результате пришел к выводу, что надежнее всего - "ручной" анализ строчного представления исходного числа.


30-04-2008 07:23
сообщение от автора материала
Привожу свою функцию корректного округления.

И чем она лучше RoundTo?


30-04-2008 06:58
Привожу свою функцию корректного округления. Функция опробирована с параметром Dig = 1..4. Больше необходимости не было. Используется в финансовых задачах. Для научных, ижинерных целей ее тчность недостаточна, надо дописывать.


function Okrugl(aFloat: Extended; Dig: Integer): Extended;
var sFloat: string;
    i, p, x: Integer;
    MinusSign: boolean;
begin
  if Abs(aFloat) < 0.000001 then begin
    Result := 0;
    Exit;
  end;
  MinusSign := aFloat < 0;
  aFloat := Abs(aFloat);

  sFloat := FloatToStrF(aFloat, ffFixed, 18, 18);
  Dig := Abs(Dig);
  for i := 1 to Length(sFloat) do
    if not (sFloat[i] in ['0'..'9']) then sFloat[i] := DecimalSeparator;

  p := Pos(DecimalSeparator, sFloat);
  if p = 0 then begin
    Result := aFloat;
    if MinusSign then
      Result := -Result;
    Exit;
  end;
  {если не найден разделитель то уже вышли}

  x := p + Dig;
  if x >= Length(sFloat) then begin
    Result := aFloat;
    if MinusSign then
      Result := -Result;
    Exit;
  end;
  {если длина вход. числа меньше или равно длина искомого то уже вышли}

  sFloat := Copy(sFloat, 1, p-1) + Copy(sFloat, p+1, Length(sFloat) - p);
  {убрали разделитель}

  x := x-1;
  if sFloat[x+1] > '4' then
    sFloat[x] := Succ(sFloat[x]);

  {while (sFloat[x] > '9')and(x > 1) do begin}
  while (sFloat[x] > '9')and(x > 0) do begin
    sFloat[x] := '0';
    x := x - 1;
    if x > 0 then
      sFloat[x] := Succ(sFloat[x]);
  end;
  if (x = 1)and(sFloat[1] > '9') then begin
    sFloat[1] := '0';
    sFloat := '1' + sFloat;
    Inc(p);
  end;
  {откатили девятки}

  sFloat := Copy(sFloat, 1, p-1) + DecimalSeparator + Copy(sFloat, p, Length(sFloat) - p+1);
  {вставили разделитель на место}

  if x = 0 then
    sFloat := '1' + Copy(sFloat, 1, p+Dig)
  else
    sFloat := Copy(sFloat, 1, p+Dig);
  {отрезали лишний хвост}

  Result := StrToFloat(sFloat);

  if MinusSign then
    Result := -Result;
end;



08-05-2007 05:49
в древности
гыгы


08-05-2007 00:58
>>> мне кажется, что в той статье проблема излишне раздута

Я считаю, что излишне раздута эмоциональная часть. Впрочем, похоже, ради нее статья и написана. А крупицу здравого смысла можно выразить словами: действительно, в современной вычислительной технике многие вещи реализованы компромиссными путями, причем далеко не лучшими, а зачастую продиктованными историческими причинами: нехваткой определенных ресурсов в древности, рыночными капризами фортуны, и многим другим...

http://www.anekdot.ru/an/an0602/s060220.html#33 (По бокам космического корабля "Кеннеди" размещаются два двигателя по 5 футов шириной....)


07-05-2007 22:26
>>>аффтар жжот, каменты рулят
согласен

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


07-05-2007 15:20
Что самое интересное, Антон Григорьев лично меня "заставил" читать данную статью! ;))
Респект автору. Действительно грамотная и занятная информация.


02-05-2007 05:53
сообщение от автора материала
Вот, нашёл ссылку в тему http://elementy.ru/blogs/users/vladyur/10157/
Короче, аффтар жжот, каменты рулят :))))


16-04-2007 03:33
Да, про "сдвиг" я был не прав.

А свои примеры я привел для подтверждения фразы
"от перемены мест слагаемых сумма изменяется"

То что Вы написали я знаю как "выравнивание порядков", т.е. сначала числа сводятся к одному порядку, а потом складываются мантиссы.

>>>Да, таких мест может быть много
Я хотел, чтобы моё сообщение сподвигло Вас написать продолжение. ;-)


14-04-2007 05:58
Антону спасибо, я уже многое забыл из этого, а раньше ведь знал на изусть добрую часть этой статьи.
Написано хорошо и добротно, ставлю 5.


14-04-2007 00:16
сообщение от автора материала
при вычислении маш.епс. делить надо на 2. Но Вы не акценитировали почему.

Вот цитата из статьи: Прибавим к числу 1.00 число 1.00E-4. Если бы всё было честно, мы получили бы 1.0001. Но у нас ограничена точность, поэтому мы вынуждены округлять до трёх значащих цифр. В результате получается 1.00. Другими словами, мы прибавляем к единице некоторое число, большее нуля, а в результате из-за ограниченной точности получаем снова единицу.

Понятно, что если бы мы прибавляли не 1.00E-4, а 9.99E-4, ничего бы не изменилось - число 1.000999 всё равно округлилось бы до 1.00. Т.е. роль играет только старшая значащая цифра, причём даже не сама цифра, а то, какой разряд является старшим. В двоичной системе, где старший разряд обязательно равен единице, уж точно играет роль только значение экспоненты, т.е. если число с заданной экспонентой добавить к единице, то получится либо единица (если число меньше машинного эпсилон), либо не единица, и конкретное значение мантиссы здесь ничего не меняет.

Из этого легко видеть, что машинное эпсилон должно иметь вид 2^N, где N - какое-то целое отрицательное число. На всякий случай напомню определение: НАИМЕНЬШЕЕ положительное число, которое при добавлении его к единице даёт результат, не равный единице, называется машинным эпсилон. При заданной экспоненте наименьшим числом будет как раз 2^N. Поэтому мы начинаем с 1 (т.е. 2^0) и делим это число на два, чтобы найти именно степень двойки.

Насколько я помню деление на 2 выполняется сдвигом даже для вещественных чисел.

Ну, вам уже сказали, что это не так. Для нормализованной записи сдвигать нужно только экспоненту, не трогая мантиссу, для денормализованной - только матиссу, не трогая экспоненту. А так как мантисса и экспонента не выровнены на границу байта, то сдвигать их обычными иструкциями процессора - занятие неблагодарное. Так что пусть уж лучше этим сопроцессор занимается.

Есть ещё подводные камни с этими "противными" вещественными числами
Например, "от перемены мест слагаемых сумма изменяется"
а виновато выравнивание порядков


Да, таких мест может быть много. На всякий случай поясню ваш пример. Вещественное число хранит фиксированное число разрядов, а экспонента, по сути дела, указывает, в каком диапазоне лежат эти разряды. Т.е., например, 23 бита мантиссы могут хранить 23 двоичных разряда в диапазоне от 2^10 до 2^-42, а могут - от 2^-80 до 2^-132 - это зависит от значения экспоненты. Другими словами, в большом диапазоне возможных значений есть относительно небольшое "окно", которое может перемещаться по этому диапазону. Верхняя граница этого окна выравнивается на старший разряд. Соответственно, младшие разряды, отстоящие слишком далеко от старшего, не попадают в окно и отбрасываются.

В первом варианте мы изначально работаем с маленькими числами, поэтому окно выровнено так, чтобы вмещать в себя их с достаточной точностью. За счёт многократных сложений число увеличивается на 6 десятичных порядков (около 20 двоичных), но так как каждый раз прибавляется маленькое число, окно перемещается постепенно, и существенной потери точности не происходит. И когда мы прибавляем сумму к большому числу, они оба умещаются в одном окне.

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


13-04-2007 00:58
2 Yashin
да, наверно так и есть


10-04-2007 09:29
>>>Насколько я помню деление на 2 выполняется сдвигом даже для вещественных чисел.
Вы ошибаетесь.


09-04-2007 06:39
Спасибо.
Очень грамотно.
Антон!
при вычислении маш.епс. делить надо на 2. Но Вы не акценитировали почему.
Кроме двоичного представления данных
Насколько я помню деление на 2 выполняется сдвигом даже для вещественных чисел.
Поправьте, если я не прав

В порядке предложения, не критики.
Есть ещё подводные камни с этими "противными" вещественными числами
Например, "от перемены мест слагаемых сумма изменяется"
а виновато выравнивание порядков

procedure TForm1.Button1Click(Sender: TObject);
var
  R2 : Single;
  i : integer;
begin
  R2 := 0; // к 0 прибавляем 1000000 раз 1Е-6
  for i := 1 to 1000000 do
    R2 := R2 + 0.000001;
  R2 := R2 + 1000000.0; // к рез-ту прибавляем 1000000
  if R2 = 1000001.0 then
    Label1.Caption := 'равно'
  else
    Label1.Caption := 'не равно';
  Label2.Caption := FloatToStr(R2);
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  R2 : Single;
  i : integer;
begin
  R2 := 1000000.0; к 1000000 прибавляем 1000000 раз 1Е-6
  for i := 1 to 1000000 do
    R2 := R2 + 0.000001;
  if R2 = 1000001.0 then
    Label1.Caption := 'равно'
  else
    Label1.Caption := 'не равно';
  Label2.Caption := FloatToStr(R2);
end;




15-08-2006 06:59
Замечательная статья!
Могу добавить только следующее: если важно (например, в финансовых вычислениях), чтобы вычисленный результат был в точности такой же, как и при ручном счёте, на бумажке, можно производить вычисления в десятичном представлении.
Для этого в Delphi есть модуль FMTBcd, который позволяет производить 4 арифметических действия над числами в так называемом двоично-десятичном представлении (BCD = Binary Coded Decimal). Подробнее можно посмотреть в справке Delphi или покопаться в интерфейсной части исходного кода модуля FMTBcd.pas.


30-07-2006 17:38
> Не обольщайтесь - там не 0.1 даже при использовании Extended, не говоря уже про Double. Не может число 0.1 быть представлено конечной двоичной дробью. Просто отладчик при показе слегка округляет число, чтобы оно выглядело не так страшно (на мой взгляд, зря он это делает, т.к. путаница от этого только увеличивается, но что есть, то есть).

По-видимому, использует FloatToStr - она не с макс. точностью выводит.


10-05-2005 20:09
To Антон Григорьев:

> ... То, что не все коенчные десятичные дроби точно представимы
> в двоичной арифметике, на результат не влияет

Лучшая новость за месяц. Спасибо.

Контроллеры - GE Fanuc (у меня например VersaMax). Так-же Сиеменсовские (Siematic C-300? не уверен). Вероятно какие-нибудь еще...
Ссылка на характеристики VersaMax: http://www.advantekengineering.ru/versamax.htm (раздел ЦПУ)
Post Scriptum О простоте использования - врут. :)
Post Post Scriptum Извиняюсь за офтоп.


07-05-2005 05:37
сообщение от автора материала
Дмитрию:

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

P.S. А разве тип real поддерживается какими-то контроллерами аппаратно? Нникогда о таком не слышал.


01-05-2005 12:00
Статья понравилась, хотя многое знал.

Вопрос имею. Можно ли рассчитать максимальную погрешность при использовании допустим real'ов в уравнении с известными операторами?

Конкретно - мне сейчас необходимо совершить порядка 400 действий типа сложение-умножение-экспонента-мать-ее-так, и я абсолютно не уверен, что результирующая точность будет приемлимой.
Дабла хватает. Но его в этом [бииип] ПЛК нету.


03-02-2005 04:12
сообщение от автора материала
Тем, кто заинтересовался темой изменения контрольного слова некоторыми dll, рекомендую посмотреть ответы на вопрос №22835 http://www.delphikingdom.com/asp/answer.asp?IDAnswer=22835

vkram там написал достаточно интересный ответ и привёл полезные ссылки.


04-12-2004 14:06
сообщение от автора материала
Последнее сообщение написал я. Не знаю, почему оно осталось неподписанным - я нормально залогинился.

Григорьев Антон


04-12-2004 10:37
У меня вот еще какой вопросик: процессор AMD Athlon, как он относится к вещественным числам?

Так же, как и все остальные. Стандарты IEEE едины для всех процессоров, в т.ч. и тех, которые несовместимы с Intel.

x:=0.1;

Если х - Double, то в отладке смотрим, х получился ровно 0.1 Ну, это понатно - проц работает в режиме Double.


Не обольщайтесь - там не 0.1 даже при использовании Extended, не говоря уже про Double. Не может число 0.1 быть представлено конечной двоичной дробью. Просто отладчик при показе слегка округляет число, чтобы оно выглядело не так страшно (на мой взгляд, зря он это делает, т.к. путаница от этого только увеличивается, но что есть, то есть).

Для проверки попытайтесь вычислить, чему равно 10*x-1 при типе Double - там будет не 0. Неточность возникнет именно потому, что x изначально неточно предсталял 0.1, потому что остальные числа в этом выражении - целые, которые представляются точно.

Кстати, если x будет иметь тип Extended, то в результате вы получите 0. Но не потому, что в этом случае x буде точно 0.1, а потому, что при умножении этого числа на 10 процессор использует соответствующее округление.
Сообщение не подписано


02-12-2004 11:04
Прошу прощения за свою глупую писанину - "на счет решения проблемы" . Так можно лишь добавить себе больше проблем. Я еще не совсем осознал проблемы, когда писал это. Еще раз извиняюсь.
Сообщение не подписано


01-12-2004 08:40
Насчет решения проблемы... Сам я начинающий программер, так что не взыщите..Буду рад Вашим отзывам!!!
Конечно, проблемы не будет, если не смешивать типы представления вещественных чисел. Но если, например, вам нужно представить число 0.2475 в виде Double (проц работает в режиме "полного" Extended) то при выполнении

var x: Double;
begin
x:=0.2475;

x получит не 0.2475 , a 0.2474999748

Чтобы не исказить значение 0.2475 , нужно перед присвоением округлить 0.2475 до 4-го знака после запятой(именно столько знаков в нашем числе) по правилам математики, затем, используя Trunc , отсечь "лишние" разряды.
Как вам решение?
Конечно, на каждый случай - свое решение, но все-таки буду рад Вашей критике.


01-12-2004 05:14
Вот код в Delphi:

x:=0.1;

Если х - Double, то в отладке смотрим, х получился ровно 0.1 Ну, это понатно - проц работает в режиме Double.
Если х - Single - то в отладке мы видим
0.10000000149 , с этим тоже все ясно...
Но когда х - Currency, мы опять видим, что х принял значние 0.1 Из справки узнаю, что Currency имеет 4 знака после запятой, значит при переводе в Extended мы получим 0.1000 , но ведь число 0.1 из правой части выражения тоже переводится в Extended и оно будет равно 0.10000000000001 примерно (точно не знаю) , так почему же Currency и Extended равны?


30-11-2004 11:54
СПАСИБО ЗА СТАТЬЮ!!!
У меня вот еще какой вопросик: процессор AMD Athlon, как он относится к вещественным числам? К сожалению, не очень разбираюсь в маркировках, а то бы и сам посмотрел. Маркировку щас не привожу, если нужна - позже скажу, но, наверное здесь имеет место общий случай для какого-то типа процессоров.


05-10-2004 18:38
Статья хорошая.
А вопросы на КС продолжают сыпаться...

offtopic:
Надо как-то заставить вновь прибывающих пройтись по материалам сайта прежде чем позволить им задавать вопросы


01-06-2004 17:45
Кстати, был весьма огорчён, что нельзя в Паскале написать

if R = Single(0.1) then...

IMHO неоправланная и гадкая подстава.


16-05-2004 19:23
Могу сказать, почему у вас не сошлись результаты тестов на машинный эпсилон. Управляющее слово меняют НЕКОТОРЫЕ функции WinAPI, а не все. В одном случае эти функции могли вызываться в процессе работы программы - в другом нет. Следовательно, результат зависит от того, что именно делают другие процессы.


19-02-2004 17:37
сообщение от автора материала
Нет, всё правильно: AND сбрасывает в ноль биты 8 и 9, оставляя другие биты без изменения, а OR устанавливает 9 бит в 1, оставляя все другие (в т.ч. и восьмой) биты без изменения. В результате в битах 9 и 8 будет 1 и 0 соответственно, т.е. 53-битная точность. А OR 300h включит максимальную, 64-битную точность.


19-02-2004 15:57
Извиняюсь, мне кажется в статье неточность

Чтобы, например, установить 53-значную точность, не изменив при этом другие биты ....
asm
FNSTCW MyCW
AND     MyCW,0FCFFh
OR      MyCW,200h
FLDCW MyCW
end;


На самом деле этот код отключит точность, надо OR      MyCW,300h


22-01-2004 10:25
поставил 8 - c double работает нормально, с single нет. Видно на счет double поправили сравнение.


21-01-2004 13:18
Как с такими вещами работают последние компиляторы 7 и 8, просто у меня стоит 6 - всё сходится. Но ни одной описанной проблемы в с# не обнаружено, может уже и борланд исправился.


09-01-2004 07:35
Пришел с проблемой ушел с решением.

Проблема была с FloatToStr. После замены на FloatToStrF все проблемы решились.

PS:
flNum:=803.2;
FloatToStr(flNum)='803.19999999978'
Вот такой глюк.

Спасибо.


26-06-2003 22:42
Нужная статья. Спасибо за труд.


20-06-2003 13:28
Отличня статья!


03-04-2003 11:20
Наиболее интересным показалось обсуждение статьи


10-01-2003 15:29
сообщение от автора материала
Для Maxim:

Это место содержит ещё одну, более важную неточность. Если все биты в экспоненте равны нулю, то это означает не 0, а -127, поэтому формально эта комбинация соответсвует числу 0*2^(-127). Упустил я этот момент, каюсь.


06-01-2003 01:14
Цитата автора:
>>>С учётом всех этих правил комбинация, когда и в мантиссе, и в экспоненте все биты равны нулю, даёт 0^0. С математической точки зрения эта комбинация бессмысленна, но разработчики формата договорились считать такую комбинацию нулём.

Это неверно, когда в мантиссе и экспоненте нули, то получится 0*2^0. С математической точки зрения эта комбинация имеет смысл. Иначе проверка на ноль занимала бы дополнительное время.


28-11-2002 13:02
сообщение от автора материала
Для Konstantin:

>>>Существует ли проблема представления десятичных чисел в других операционных системах (напр. UNIX)? Имеются ли такие проблемы у других языков и других трансляторов (C,FORTRAN)? Как там решаются аналогичные проблемы?

Боюсь, вы зря читали статью. Повторяю ещё раз то, что вы не поняли: все проблемы с вещественными числами возникают из-за особенностей их аппаратной обработки. Язык, ОС и т.д. ни на что не влияют.

>>>Автору следовало бы указать хотя бы некоторые решения данной проблемы. Тем более, что в DELPHI они есть, но в документации внимание на них не акцентируется.

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

>>>Возьмем первый пример из статьи:
>>>var R:Single;
>>>begin
>>>  R:=0.1;
>>>  Label1.Caption:=FloatToStr(R)
>>>end;
>>>Изменим функцию FloatToStr(R) на FloatToStrF(R,ffFixed,7,3) и получите точно 0.1

Ерунду пишете. Смысл этого примера - не вывести на экран строку "0.1", а показать, что в результате выполнения оператора "R:=0.1" в переменной R оказывается не 0.1, а другое число. И использование FloatToStrF в данном случае - это не решение проблемы, а сокрытие, сама же проблема может всплыть в другом месте, не связанном с выводом на экран.

>>>Замените R=0.1 на SameValue(R,0.1,1E-7) и результат будет "Равно"


Это где же в Delphi 5 вы нашли функцию SameValue? А более новых версий Delphi на момент написания статьи не было.


11-11-2002 03:26
Статья очень полезна для не программистов, которые должны много программировать по роду своей деятельности (хотя и несколько длинновата и туманно написана). Профессионалы это и так обязаны знать. Автору не стоит забывать, что все мы чайники, но только каждый в своей области.

По сути.

-  Существует ли проблема представления десятичных чисел в других операционных системах (напр. UNIX)? Имеются ли такие проблемы у других языков и других трансляторов (C,FORTRAN)? Как там решаются аналогичные проблемы?

- Автору следовало бы указать хотя бы некоторые решения данной проблемы. Тем более, что в DELPHI они есть, но в документации внимание на них не акцентируется.

Возьмем первый пример из статьи:
var R:Single;
begin
  R:=0.1;
  Label1.Caption:=FloatToStr(R)
end;
Изменим функцию FloatToStr(R) на FloatToStrF(R,ffFixed,7,3) и получите точно 0.1

Второй пример:
var R:Single;
begin
  R:=0.1;
  if R=0.1 then
   Label1.Caption:="Равно"
  else
   Label1.Caption:="Не равно"
end;
Замените R=0.1 на SameValue(R,0.1,1E-7) и результат будет "Равно"



17-04-2002 08:50
Даааа.... А я то думал, что я опытный программист...
Спасибо!


29-03-2002 17:46
Так становятся волшебниками.

С благодарностью.
vb-программер.


23-11-2001 10:08
сообщение от автора материала
Насчёт того, почему 0.1*5=0.5. Если мы будем тупо на бумажке умножать 0.1 во внутреннем представлении компьютера, то получим двоичную дробь, хотя и конечную, но всё же не влезающую в отведённые для неё разряды. Поэтому сопроцессор вынужден её округлять. Видимо, разница мажду 0.5 и получившимся значением столь незначительна, что при округлении пропадает. Кроме того, режим округления сопроцессора можно менять с помощью управляющего слова. Я подозреваю, что по умолчанию устанавливается такой режим, когда сопроцессор пытается подчистить ошибки округления. Точнее это можно узнать в документации на сопроцессор, которой у меня, к сожалению, нет.


22-11-2001 17:00
Извиняюсь, не подписал предыдущее сообщение  ;0)


22-11-2001 14:17
Привет!
спасибо за статью.
есть неясность с extended.
например :

var r : extended;
begin
  r := 0.1;
  if r*5=0.5 then ShowMessage("равно") else ShowMessage("не равно")
end;

почему в результате возвращается 'равно', хотя вроде бы число 0.1 не может быть представлено точно ни в одном вещественном типе (в т.ч. в extended), тогда как 0.5 может быть точно представлено. Следовательно число приблизительно равное (0.1*5) не должно быть равно точному представлению 0.5?
Сообщение не подписано


16-08-2001 18:17
Спасибо! Очень помогли.


28-06-2001 01:31
Очень полезный материал. Спасибо! Я начинающий программер, и для меня такое поведение машины было бы просто колдовством! Слава богу, что я не успел с ним столкнуться, а то я бы изгнал из нее бесов, чем нибудь тяжелым... Тем более что мой родной язык ASM для переферийных контроллеров, и там я привык к однозначности на математических операциях. Ругателям статьи скажу что дело не всегда в том с какой точностью выдан результат. Простой пример, переход по проверке условия
XOR  literal and W
GOTO IF ZERO
поведение системы будет не предсказуемо, если не учитывать вышеизложенный материал. Еще раз спасибо.


08-06-2001 15:49
сообщение от автора материала
С помощью своих знакомых я провёл некоторые дополнительные эксперименты и обнаружил, что из 11 проверенных таким образом машин с Windows 95/98 эффект потери точности был замечен на четырёх. Причём никакой корреляции между наличием эффекта и версией Windows установить не удалось. Также пока не удалось связать потерю точности с аппаратной конфигурацией и установленными программами. К сожалению, среди четырёх компьютеров с этим эффектом два - это те, которые мне наиболее доступны: мой домашний и рабочий. Так как при написании статьи я экспериментировал в первую очередь именно с этими машинами, я и сделал в ней столь категоричный вывод.

Однако эксперименты преподнесли один весьма неожиданный сюрприз: компьютер Королевы, на котором стоит NT, тоже продемонстрировал эффект снижения точности. Самое интересное - что этот компьютер участвовал и в тех экспериментах, которые проводились при подготовке статьи, и никакого снижения точности не наблюдалось. Система на компьютере не переустанавливалась, только доустанавливались некоторые программы, хотя на других компьютерах с такими же программами эффекта нет. Так что со снижением точности всё не так однозначно, как я написал в статье. Тем не менее, он существует, и даже встречается не слишком редко, хотя причину его я сейчас указать не могу.


17-05-2001 19:54
Я перепроверил своё утверждение на трёх различных WINDOWS 9x:
4.0.1111 B
4.10.1998
4.10.2222 A
и ни где указанного автором статьи бага не обнаружил.

Антон, если Вас не затруднит, то укажите пожалуйста,
при каких обстоятельствах Вы это наблюдали.

Если верить Вам, то использование FPU под MS WIN являeтся
недопустимой роскошью :)

P.S. Сейчас я убегаю, но возможно смогу прокомментировать Ваш ответ позже.
V.


17-05-2001 12:50
сообщение от автора материала
Насчёт непонятной фразы прошу прощения. Редактировал несколько раз и вот доредактировался. Правильно она должна звучать так: 'Впрочем, напоминаю, что использование Set8087CW нежелательно, так как эта функция влияет на значение переменной Default8087CW, а это значение может понадобиться'.

Что касается того, что 'автор не прав'. Витёк, вы меня что, совсем безответсвенным идиотом считаете, что позволяете себе заявлять так безапелляционно?! Уж наверное, прежде чем написать такое, я это проверил на разных компьютерах. И не только я (см. благодарность Елене Филипповой в конце статьи). И вопрос этот был задан на борландовской конференции. И, судя по ответам, эта проблема знакома не только мне. Заодно просмотрите другие отзывы: люди тоже сталкивались с такой проблемой. Я вполне допускаю, что, начиная с какой-то подверсии Windows 98 эта ошибка исправлена, и вам попалась именно такая версия. Советую не забывать: по одному эксперименту нельзя судить о явлении в целом, надо проделать серию экспериментов.

А чтобы несколько уменьшить вашу уверенность по поводу того, что 'что любые версии WINDOWS (от 3.1) корректно сохраняют управляющее слово сопроцессора', настоятельно рекомендую вам повнимательнее посмотреть исходные коды VCL. В частности, модуль Dialogs.pas, функцию TCommonDialog.TaskModalDialog. Вот кусок кода из неё (комментарии не мои, а оригинальные от Borland"а):

asm
// Avoid FPU control word change in NETRAP.dll, NETAPI32.dll, etc
FNSTCW  FPUControlWord
end;
try
CreationControl := Self;
Result := TDialogFunc(DialogFunc)(DialogData);
finally
asm
  FNCLEX
  FLDCW FPUControlWord
end;
Application.UnhookMainWindow(MessageHook);
end;

Аналогичный код (правда, без комментариев) можно найти и в некоторых других местах исходников (я просматривал исходники Delphi 5.0 (Build 6.18) Update Pack 1).

От вас, Витёк, я жду либо аргументированных возражений, либо публичных извинений.


16-05-2001 19:05
Опять извиняюсь, но в предыдущем сообщении я очепятался в своём почтовом адресе :((


16-05-2001 19:02
Премного извиняюсь, с уважением и пр., но !!!
По поводу “плюхи” от MS.
Я повторил на WIN98SE четвёртый пример из статьи, и обнаружил следующие:

Single даёт -7,3015691270939E-8;
Real даёт -1,11413101102709E-12; при {$REALCOMPATIBILITY ON}
double даёт 1,44327637948555E-16
extended даёт -6,7762635780344E-20

Легко заметить, что утверждение автора о глюке ошибочно.

Не сомневаюсь, что любые версии WINDOWS (от 3.1) корректно сохраняют управляющее слово сопроцессора, и не дают другим приложениям его испортить. Впрочем, могут ли это сделать драйвера – я не знаю.

Заодно добавлю, мне совершенно не понятна фраза из статьи:
“Впрочем, напоминаю, что использование нежелательно, Default8087CW.”
V.


15-03-2001 22:26
Спасибо.


15-03-2001 09:24
Статья очень понравилась.Побольше бы таких.


14-03-2001 10:59
Очень полезная статья.
 JINX


14-03-2001 09:14
Я сталкивался с тем, что моя математическая программа в разных ОС работает по разному, и теперь понял почему! - благодаря плюхе от M$


13-03-2001 21:52
Очень позновательно :)


13-03-2001 21:39
Весьма интересно и полезно.


Добавьте свое cообщение

Вашe имя:  [Войти]
Ваш адрес (e-mail):На Королевстве все адреса защищаются от спам-роботов
контрольный вопрос:
Вода мокрая или сухая?
в качестве ответа на вопрос или загадку следует давать только одно слово в именительном падеже и именно в такой форме, как оно используется в оригинале.
Надоело отвечать на странные вопросы? Зарегистрируйтесь на сайте.

Оценка содержания
 
Содержит полезные и(или) интересные сведения
 
Ничего особенно нового и интересного
 
Написано неверно (обязательно укажите почему)


Оценка стиля изложения
 
Все понятно, материал читается легко
 
Есть неясности в изложении
 
Непонятно написано, трудно читается

Текст:
Жирный шрифт  Наклонный шрифт  Подчеркнутый шрифт  Выравнивание по центру  Список  Заголовок  Разделительная линия  Код  Маленький шрифт  Крупный шрифт  Цитирование блока текста  Строчное цитирование
  • вопрос Круглого стола № XXX

  • вопрос № YYY в тесте № XXX Рыцарской Квинтаны

  • сообщение № YYY в теме № XXX Базарной площади
  • обсуждение темы № YYY Базарной площади
  •  
     Правила оформления сообщений на Королевстве
      
    Время на сайте: GMT минус 5 часов

    Если вы заметили орфографическую ошибку на этой странице, просто выделите ошибку мышью и нажмите Ctrl+Enter.
    Функция может не работать в некоторых версиях броузеров.

    Web hosting for this web site provided by DotNetPark (ASP.NET, SharePoint, MS SQL hosting)  
    Software for IIS, Hyper-V, MS SQL. Tools for Windows server administrators. Server migration utilities  

     
    © При использовании любых материалов «Королевства Delphi» необходимо указывать источник информации. Перепечатка авторских статей возможна только при согласии всех авторов и администрации сайта.
    Все используемые на сайте торговые марки являются собственностью их производителей.

    Яндекс цитирования