Как добавить эпсилон при округлении значения в Delphi, чтобы избежать противоречивых результатов?

Я столкнулся с проблемой округления в Delphi, когда значения, очень близкие к средней точке (например, 21,499991254), иногда округляются в меньшую сторону, а не в большую, что может привести к несогласованности в моих расчетах.

Например:

var
  value1, value2: Double;
begin
  value1 := 21.5;
  value2 := 21.499991254;
  ShowMessage(IntToStr(Round(value1))); // Returns 22, as expected
  ShowMessage(IntToStr(Round(value2))); // Returns 21, which is not ideal for my case
end;

Я понимаю, что добавление небольшого значения эпсилон перед округлением может помочь, например:

Round(Value + Epsilon)

Но мне интересно, есть ли в Delphi более надежный или стандартный подход для решения этой ситуации? Существуют ли какие-либо встроенные функции или рекомендации, обеспечивающие единообразное округление фактически одинаковых значений (например, 21,499991254 и 21,5)?

Любые советы или идеи будут очень признательны!

Это зависит от характера ваших расчетов, для чего они используются?

whosrdaddy 27.08.2024 22:17

прямо сейчас я использую его для графики, чтобы выровнять изображение по позиции пикселя. когда я прошу выровнять по позиции пикселя квадрат 21,5*21,5, он выравнивается по 22x22 на экране, но если это какой-то эпсилон, например 21,49999x21,49999, который визуально точно такой же, как 21,5x21,5, он округляется до 21x21, что не то же, что 22х22 :(

zeus 27.08.2024 23:09

Обычным соглашением для округления является округление менее 0,5 в меньшую сторону и более 0,5 в большую сторону. Ровно 0,5 округления до ближайшего четного целого числа. Ваши примеры демонстрируют именно такое поведение. Всегда должна быть точка отсечения, где раунд переключается снизу вверх, поэтому я не знаю точно, чего вы надеетесь достичь. Такова природа Раунда.

Dsm 28.08.2024 00:02

Почему 21,5*21,5? Что означают эти цифры? Если бы оно было 21х21, оно тоже будет округлено до 21х21, а не до 22х22.

jsmith 28.08.2024 04:43
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
4
87
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Как отмечали другие, результаты, которые вы получаете, верны. В зависимости от того, сколько цифр вы хотите принять во внимание, вы можете сделать что-то вроде:

Round(Round(Value2 * 10) / 10);

{ What this is doing:                  }

{ Move the decimal place to the right: }
{   21.499991254 * 10 = 214.99991254   }

{ Round the decimal places:            }
{   Round(214.99991254) = 215.0        }

{ Move the decimal place back:         }
{   215.0 / 10 = 21.5                  }

{ Round the decimal places:            }
{   Round(21.5) = 22.0                 }

Если вы хотите, чтобы округление было более точным, увеличьте количество десятичных знаков, используемых в разделах *10 и /10.

Обратите внимание, что округление в сторону ближайшего четного числа по-прежнему действует для младшей значащей цифры в расчете. Итак, 21.4999 вернет 22, а 20.4999 вернет 20.

Если вы хотите всегда округлять числа, оканчивающиеся на .5 и выше, я думаю, вам понадобится оператор if, например:

if Frac(Value) >= 0.5 then
begin
  Result := Ceil(Value);
end
else
begin
  Result := Floor(Value);
end;

При этом я новичок в Delphi, поэтому у кого-то может быть лучшее решение.

Я также хотел бы добавить, что если эти значения являются результатом точности с плавающей запятой, возможно, измените тип данных вашей переменной на что-то более надежное:

Decimal1: Single;   //  7  significant digits, exponent   -38 to +38
Decimal2: Currency; // 50+ significant digits, fixed 4 decimal places
Decimal3: Double;   // 15  significant digits, exponent  -308 to +308
Decimal4: Extended; // 19  significant digits, exponent -4932 to +4932

Подробности о типах данных из Delphi Basics.

В 64-битных приложениях (то есть в большинстве современных приложений) Extended — это просто синоним Double.

Andreas Rejbrand 28.08.2024 13:48

Другие вопросы по теме