Round — Функция Delphi


Delphi-Help

Round

Round

Описание

function Round ( const Number : Extended ) : Int64;

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

Округление использует банковские правила, где точная половина значения вызывает округление к четному числу:

12.4 округляется до 12
12.5 округляется до 12
12.6 округляется до 13

13.4 округляется до 13
13.5 округляется до 14
13.6 округляется до 14

Пример кода

begin
ShowMessage(‘Round(12.75) = ‘+IntToStr( Round (12.75)));
ShowMessage(‘Trunc(12.75) = ‘+IntToStr(Trunc(12.75)));
ShowMessage(‘ Int(12.75) = ‘+FloatToStr(Int(12.75)));
ShowMessage(‘ Frac(12.75) = ‘+FloatToStr(Frac(12.75)));
end;

Round(12.75) = 13
Trunc(12.75) = 12
Int(12.75) = 12
Frac(12.75) = 0.75

Примечание

Функция Trunc делает то же самое, но возвращает целое число в Integer значении.

Как научить Delphi делать правильное округление дробных чисел

Delphi , Синтаксис , Математика

Как научить Delphi делать правильное округление дробных чисел

Автор: Lonely Wanderer

Чем отличается законченный программист от простого пользователя? Пользователь думает, что в килобайте 1000 байт, а Программист думает, что в километре 1024 метра.

Еще более короткий способ:

Округление дробных чисел с точностью i — количество знаков после запятой, S — дробное число в строковом виде.

Статья Как научить Delphi делать правильное округление дробных чисел раздела Синтаксис Математика может быть полезна для разработчиков на Delphi и FreePascal.

Комментарии и вопросы

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

Override Delphi функция System.Round

Я только что обнаружил, что программное обеспечение я должен переопределять использует extensivelly System.Round (). Проблема заключается в том, что эта функция использует «Банкиры округление» и поведение не может быть изменено как в Math.RoundTo () (rmDown, rmUp, rmNearest, rmTruncate).

Я должен изменить поведение «нормальное округление» (12,5 -> 13 НЕ 12,5 -> 12) . Так что я хотел бы переопределить System.Round () в глобальном масштабе. Я хочу, чтобы это сделать, потому что Round () используется так много раз, и я не хочу, чтобы изменить их все вручную.

Как это возможно?

ВНИМАНИЕ : Несмотря на то, что ответ ниже рассматривается вопрос который был задан, я рекомендовал бы , чтобы никто никогда не использует его. Если вы хотите выполнить закругление иначе, а Round затем написать и вызвать специальную функцию.

Вы можете использовать во время выполнения кода крюк , чтобы изменить реализацию Round .

Морщин является то , что это немного сложно достать адрес Round функции , хотя , потому что она является внутренней. Вы также должны быть осторожны , чтобы следовать соглашению вызова используется. Входное значение передается в регистре стека x87 ST(0) и возвращаемое значение представляет собой 64 — битное целое в EDX:EAX .

Вот как это сделать.

Если вы хотели бы реализовать свою версию в Pascal , чем ASM , то вам необходимо адаптировать нестандартное соглашение о вызовах в _ROUND стандартную Delphi вызывающей конвенции. Как это:

Обратите внимание, что я предположил, что здесь, что ваша программа ориентирована на 32 бит. Если вы должны быть ориентированы на 64 бита, то эти принципы так же, но детали явно отличаются.

подскажите функцию в Delphi округления до 2-х знаков после запятой

Не поленился в справку слазил.
Expression Value

RoundTo(1234567, 3) 1234000
RoundTo(1.234, -2) 1.23
RoundTo(1.235, -2) 1.24
RoundTo(1.245, -2) 1.24

КонецЦикла

(7) можно поподробнее, как обойтись раундом?
я сейчас использую следующее :

temp := tarif_obhoz.value * Ploch_all.value;
obhoz_S.value := StrToFloat(FloatToStrF(temp,ffFixed,15,2));

работает правильно, только как-то не красиво .

Round — Функция Delphi

Профиль
Группа: Комодератор
Сообщений: 5273
Регистрация: 4.2.2005
Где: Гомель, Беларусь

Репутация: 53
Всего: 133

Значит так друзья. Пишу программу и столкнулся с «детской» процедуркой. Код немного переделанный (что бы понятно было), но суть ясна. Значит вот он:

Код
var
str: string;
x,y: real;
begin
x:= 0.5;
y:= 1.5;
str:= IntToStr( Round(x) + Round(y) );
Label1.Caption:= str ;
end;

Так вот сколько вы ожидаете увидеть в Label1? А ожидал 3. А там 2 !
Вроде встречался с функцией Round и знаю, что она должна округлять по извесным правилам. Т.е. 0.5 округляется до 1, а 1.5 до 2. Но почему же тогда 1 + 2 = 2 ?
Может кто встречался с таким? Это хорошо, что у меня так получилось, что я сразу нашел. А ведь из-за этого погут все расчеты пойти на смарку.

Профиль
Группа: Участник Клуба
Сообщений: 3497
Регистрация: 31.3.2002
Где: Лес

Репутация: 14
Всего: 115

RA
Дата 10.3.2005, 04:40 (ссылка) | (нет голосов) Загрузка .
Цитата(Poseidon @ 10.3.2005, 03:41)
Т.е. 0.5 округляется до 1

0.5 округляется до 0.

Добавлено @ 04:45
попробуй вместо Round использовать Ceil, ибо чеил округляет число в сторону увеличения, а round до ближайшего целого.

//Округление в сторону увеличения
function Ceil(const X: Extended): Integer;
begin
Result := Integer(Trunc(X));
if Frac(X) > 0 then
Inc(Result);
end;

//Округление в сторону уменьшения
function Floor(const X: Extended): Integer;
begin
Result := Integer(Trunc(X));
if Frac(X)

Это сообщение отредактировал(а) RAdmin — 10.3.2005, 04:56

Профиль
Группа: Участник
Сообщений: 442
Регистрация: 5.11.2004
Где: Краснодар

Репутация: 2
Всего: 6

Из того модуля Math:

SPrograMMer
Дата 10.3.2005, 17:48 (ссылка) | (нет голосов) Загрузка .
Код
Function RoundTo(const AValue:Double;const ADigit:TRoundToRange):Double;

Попробуй сказать:

Код
.
str:= IntToStr( RoundTo(x,0) + RoundTo(y,0) );
.

Профиль
Группа: Комодератор
Сообщений: 5273
Регистрация: 4.2.2005
Где: Гомель, Беларусь

Репутация: 53
Всего: 133

RAdmin

Poseidon
Дата 11.3.2005, 04:05 (ссылка) | (нет голосов) Загрузка .
Цитата
0.5 округляется до 0.

Это в этой процедуре. А если мы, к примеру, возьмем

Код
x:= 1.5;
y:= 2.5;

то получим не желанные 5, а 4!

Цитата
попробуй вместо Round использовать Ceil

Не подходит! Нужно именно округлять по правилам ( =5 в большую сторону). Конечно можно устроить проверку символа после запятой и по нему определять, куда округлять в большую (Ceil) или меншую (Trunc) сторону. Но все же хотелось бы разобраться, почему так происходит? Ведь по правилам 0,5 округляется до 1. А Delphi округляет до 0. При этом 1,5 округляется до 2 и 2,5 до 2 . 3,5 до 4 и 4,5 до 4 . И так далее. Что это? Ошибка Borland? Или какая-то неизвестная тонкость функции Round?

В общем-то начиная эту тему, я не ставил себе целью найти помощь. В Delphi есть ряд математических функций, каторые могут справится с округлением. Можно еще применить всякие хитрости (допустим, предложенная мной проверка символа после запятой). Целью было узнать, почему так происходит. Почему Round округляет не так, как от нее ждут?

Есть у кого мнение по этому поводу?

Профиль
Группа: Экс. модератор
Сообщений: 2776
Регистрация: 29.1.2003
Где: Екатеринбург

Репутация: 44
Всего: 64

[/B], ‘); return false;» title=»Вставить ник в поле ввода»>
Дата 11.3.2005, 06:04 (ссылка) | (нет голосов) Загрузка . ‘, ‘11.3.2005, 06:04’, 347761);»>
Цитата
Целью было узнать, почему так происходит. Почему Round округляет не так, как от нее ждут?

Это не ошбика Борладн это особенность представления дробных чисел в FPU, и, если я правильно помню, то именно от того, что 0.5(и некоторые другие, приведенные тобой числа), не могут быть представлены конечной двоичной дробью, в результате чего в реальности число ставновится чем то вроде — 0.499999999999999999999999999999999.

Где то на этом форуме недавно была ссылка на статью, хорошо это обьясняющюю, но найти ее, к сожалению, я не могу.

Это сообщение отредактировал(а) — 11.3.2005, 06:06

Профиль
Группа: Экс. модератор
Сообщений: 4147
Регистрация: 25.3.2002
Где: Москва

Репутация: 80
Всего: 162

Alex
Дата 11.3.2005, 09:47 (ссылка) | (нет голосов) Загрузка .
Цитата
Где то на этом форуме недавно была ссылка на статью, хорошо это обьясняющюю, но найти ее, к сожалению, я не могу.

http://forum.vingrad.ru/index.php?showtopic=44001&hl=

Профиль
Группа: Модератор
Сообщений: 20425
Регистрация: 8.4.2004
Где: Зеленоград

Репутация: нет
Всего: 449

Akina
Дата 11.3.2005, 09:54 (ссылка) | (нет голосов) Загрузка .
Цитата(Poseidon @ 11.3.2005, 05:05)
При этом 1,5 округляется до 2 и 2,5 до 2 . 3,5 до 4 и 4,5 до 4 . И так далее. Что это? Ошибка Borland? Или какая-то неизвестная тонкость функции Round?

Хотя это — следствие представления числа в бине, но попутно замечу, что есть термин «бухгалтерское округление» — то что ОБЯЗАНО использоваться в любых денежных расчетах. Так вот, по нему половинка округляется не вниз и не вверх, а к БЛИЖАЙШЕМУ ЧЕТНОМУ. И это определено соотв. стандартом, международным кстати. Вот такие пироги.

О(б)суждение моих действий — в соответствующей теме, пожалуйста. Или в РМ. И высшая инстанция — Администрация форума.

Профиль
Группа: Участник
Сообщений: 232
Регистрация: 8.12.2004

Репутация: 1
Всего: 1

RST8
Дата 11.3.2005, 20:59 (ссылка) | (нет голосов) Загрузка .

Профиль
Группа: Участник Клуба
Сообщений: 1000
Регистрация: 2.12.2003
Где: Москва

Репутация: 25
Всего: 55

Петрович
Дата 11.3.2005, 22:13 (ссылка) | (нет голосов) Загрузка .
Цитата(RST8 @ 11.3.2005, 21:59)
Так чем же можно округлить число по правилам до какого нибудь знака после запятой?

Дык неужели из темы не стало ясно что все зависит от того какие правила имеются ввиду. Много их, правил-то, и все разные.

Профиль
Группа: Участник
Сообщений: 442
Регистрация: 5.11.2004
Где: Краснодар

Репутация: 2
Всего: 6

SPrograMMer
Дата 11.3.2005, 22:45 (ссылка) | (нет голосов) Загрузка .
Цитата
Дык неужели из темы не стало ясно что все зависит от того какие правила имеются ввиду. Много их, правил-то, и все разные.

У меня что-то родилось:
Пусть наше 0.5 представляется в виде 0.499999999999999999999, а нам нужно всё это округлить до целых. Поступаем следующим образом:
1. Округлаем до того знака, до которого задавли число, то есть, в числе 0.5 одна цифра после запятой => округляем до одного знака после запятой (можно RondTo`ом воспользоваться).
Вот только что при этом получится? по идее 0.49999999999999 должно превратиться в 0.5
2. Округлаем (тут же) полученное число до нужного.

Код
X:=0.5;
Y:=Round(RoundTo(X,-1)); // оч хочется что б в y стало 1.

Профиль
Группа: Участник Клуба
Сообщений: 1000
Регистрация: 2.12.2003
Где: Москва

Репутация: 25
Всего: 55

Посмотри описания функций RoundTo и SimpleRoundTo:

Петрович
Дата 12.3.2005, 00:40 (ссылка) | (нет голосов) Загрузка .
Цитата
RoundTo uses “Banker’s Rounding” to determine how to round values that are exactly midway between the two values that have the desired number of significant digits. This method rounds to an even number in the case that AValue is not nearer to either value.

The following examples illustrate the use of RoundTo:

RoundTo(1234567, 3) 1234000
RoundTo(1.234, -2) 1.23
RoundTo(1.235, -2) 1.24
RoundTo(1.245, -2) 1.24

Note: The behavior of RoundTo can be affected by the Set8087CW procedure or SetRoundMode function.

Цитата
SimpleRoundTo uses asymmetric arithmetic rounding to determine how to round values that are exactly midway between the two values that have the desired number of significant digits. This method always rounds to the larger value.

The following examples illustrate the use of SimpleRoundTo:

SimpleRoundTo(1234567, 3) 1234000
SimpleRoundTo(1.234, -2) 1.23
SimpleRoundTo(1.235, -2) 1.24
SimpleRoundTo(-1.235, -2) -1.23

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

Так что выбирай то что тебе надо.

Профиль
Группа: Участник
Сообщений: 4
Регистрация: 10.3.2005

Репутация: нет
Всего: нет

Santer
Дата 12.3.2005, 02:58 (ссылка) | (нет голосов) Загрузка .

Профиль
Группа: Комодератор
Сообщений: 5273
Регистрация: 4.2.2005
Где: Гомель, Беларусь

Репутация: 53
Всего: 133

Всем спасибо за ответы
В заключении темы хотелось бы все-таки выяснить, так есть ли в Delphi функция, каторая выполняла бы то, что задокументировано для Round ? Т.е. функция, каторая округляла бы по общепринятым правилам, а не по каким-либо бухгалтерским или еще. Чтоб =?.5 — к большему. Или все таки придется написать такую функцию самому?

Santer

Poseidon
Дата 12.3.2005, 04:37 (ссылка) | (нет голосов) Загрузка .
Цитата
Вот вам еще один способ округления: к округляемому числу прибавляем 0.5 и отбрасываем дробную часть

Интересный способ. Как говорится, все гениальное — просто. Не плохо начал (это ведь твое парвое сообщение)! Так и держи.

Профиль
Группа: Экс. модератор
Сообщений: 4147
Регистрация: 25.3.2002
Где: Москва

Репутация: 80
Всего: 162

Alex
Дата 12.3.2005, 10:57 (ссылка) | (нет голосов) Загрузка .
Цитата(Poseidon @ 12.3.2005, 04:37)
В заключении темы хотелось бы все-таки выяснить, так есть ли в Delphi функция, каторая выполняла бы то, что задокументировано для Round ? Т.е. функция, каторая округляла бы по общепринятым правилам, а не по каким-либо бухгалтерским или еще. Чтоб =?.5 — к большему. Или все таки придется написать такую функцию самому?

Ты так и не понял проблемы.

Профиль
Группа: Комодератор
Сообщений: 5273
Регистрация: 4.2.2005
Где: Гомель, Беларусь

Репутация: 53
Всего: 133

Alex

Poseidon
Дата 12.3.2005, 20:17 (ссылка) | (нет голосов) Загрузка .
Цитата
Ты так и не понял проблемы.

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

Цитата
половинка округляется не вниз и не вверх, а к БЛИЖАЙШЕМУ ЧЕТНОМУ

может потому, что

Цитата
0.5(и некоторые другие, приведенные тобой числа), не могут быть представлены конечной двоичной дробью, в результате чего в реальности число ставновится чем то вроде — 0.499999999999999999999999999999999.

может еще по какой причине.
Это действительно проблема, т.к. в книге по Delphi написано буквально следующее:

Цитата
Функция Round(n) возврещает целое, полученное путем округления n по известным правилам

Может быть следует задать авторам этой книги вопрос, а что значит «известные правила»? Думаю это будет лишним. Если спросить у любого школьника (старших классов): «К чему округлиться 3,5?», то он сразу же ответит к 4.» А к чему округлиться 3,4?» — «К трем.» Это я думаю и есть известные правила.
Даже в справке Delphi написано

Цитата
Round returns an Int64 value that is the value of X rounded to the nearest whole number

На сколько я понял, перевод этого звучит так: «Round возвращает значение Int64, каторое является значением Х, округленным к самому близкому целому числу. Коментарии я думаю излишни.

PS. Хотелось бы все таки узнать, есть ли та функция, что округлит 3,5 к 4 («по известным правилам»). Как видно Round не справляется.
Alex, лично к тебе: В одной из других тем (не так давно) ты писал следующее:

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

Так вот подскажи, может нашел что? Или может в FAQ что-нибудь есть по этому поводу? Написать такую функцию не проблема, но

Цитата
Дело в том, что достаточно грамотные и знающие программиста порой не знают о существовании таких нужных и полезных функций. Но их можно понять, эти функции появились в Delphi не так давно, по-моему, начиная с 5 версии

и

Цитата
Если уж учить так, учить и если показывать пример то самый правильный и может быть самый навороченный, но в нем учтены все подводные камни, о которых вам известно.

Как говорится: «Модератор всегда прав» (p0s0l)

Это сообщение отредактировал(а) Poseidon — 12.3.2005, 20:21

Delphi — RoundTo — always down

I need to round a floating point number to two decimal places, but always down. Now I use RoundTo(number, -2) , but it does the rounding mathematically correctly, which is undesired behavior for my situation. Let the reason, why I need to do this, aside.

I eventually achieved it using this:


However, this solution just doesn’t feel right. Is there a «mathematically clean» way to do it, without messing with strings and resetting decimal separators etc. ? I searched a lot, but didn’t find any.

2 Answers 2

You can do the following:

  1. Multiply the value by 100.
  2. Truncate to an integer, towards zero.
  3. Divide the value by 100.

I’ve assumed that by rounding down you mean towards zero. So 0.678 rounds down to 0.67 and -0.678 to -0.67. However, if you want to round towards -∞ then you should replace Trunc with Floor .

Another way to tackle the problem is to recognise that a Currency value is simply a 64 bit integer with an implicit shift of 10000. So the entire operation can be performed using integer operations, unlike the code above which uses floating point operations.

Currency is a fixed-point data type that minimizes rounding errors in monetary calculations. It is stored as a scaled 64-bit integer with the 4 least significant digits implicitly representing decimal places. When mixed with other real types in assignments and expressions, Currency values are automatically divided or multiplied by 10000.

For example you could implement RoundCurrTo2dpTruncate like this:

Note that here the arithmetic has been an shifted by 10000. So multiplication by 100 has become division by 100. And so on.

Переопределить функцию Delphi System.Round

Я только что обнаружил, что программное обеспечение, которое я должен переопределить, широко использует System.Round (). Проблема в том, что эта функция использует «округление банкиров», и поведение нельзя изменить, как в Math.RoundTo () (rmDown, rmUp, rmNearest, rmTruncate).

Мне нужно изменить поведение на «нормальное округление» (12,5 -> 13, НЕ 12,5 -> 12) . Поэтому я бы хотел переопределить System.Round () глобально. Я хочу сделать это, потому что Round () используется так много раз, и я не хочу менять их все вручную.

Как это возможно?

4 ответа

ВНИМАНИЕ : Хотя ответ ниже касается вопроса, который был задан, я бы порекомендовал, чтобы никто никогда не использовал его. Если вы хотите выполнить округление не так, как Round напишите и вызовите специальную функцию.

Вы можете использовать ловушку кода времени выполнения, чтобы изменить реализацию Round .

Проблема заключается в том, что немного сложно получить адрес функции Round хотя она является внутренней. Вы также должны быть осторожны, чтобы следовать принятому соглашению о вызовах. Входное значение передается в регистр стека x87 ST(0) а возвращаемое значение представляет собой 64-разрядное целое число в EDX:EAX .

Вот как это сделать.

Если вы предпочитаете реализовывать свою версию в Pascal, а не в asm, вам нужно адаптировать нестандартное соглашение о _ROUND к стандартному соглашению о _ROUND Delphi. Как это:

Обратите внимание, что я предположил, что ваша программа нацелена на 32 бит. Если вам нужно использовать 64-битную архитектуру, то принципы практически одинаковы, но детали явно различаются.

Функции Delphi

Стандартные функции Delphi:

Для проведения всевозможных математических вычислений и многочисленных преобразований язык программирования Delphi содержит библиотеки стандартных процедур и функций. Давайте подробнее рассмотрим стандартные функции Delphi.

Между значением и именем функции существует зависимость. Поэтому всякая функция может быть представлена как операнд некоторого выражения (к примеру, в инструкции присваивания). Для возведения числа в n-ую степень достаточно записать

откуда ln — функция, вычисляющая натуральный логарифм числа exp(x), exp — функция, вычисляющая экспоненту в степени x, x — число, n-ую степень которого надо найти, а n — степень числа x. Каждая функция обладает следующими характеристиками: тип значений, тип параметров.

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

Математические функции Delphi:

Библиотеки языка Delphi включаются в себя и множество математических функций:

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

где a выражает угол в градусах; 3.1415926 означает число pi. На месте константы 3.1415926 с дробной частью для достижения большей точности чаще всего пользуются стандартной именованной константой pi. Тогда выражения для угла в пересчете в радианы будет выглядеть следующим образом:

Функции преобразования Delphi:

Наиболее частое использование функций преобразования связано с инструкциями, которые обеспечивают ввод/вывод какой-либо информации. Например, для вывода значения переменной c типом real в поле вывода диалогового окна (компонент Label), нужно провести преобразование числа в строку символов, которая собственно изображает данное число. Это можно достичь, применяя функцию FloatToStr, которая заменяет значение выражения (оно указано как параметр функции) его строковым представлением.

Пример.

В приведенном примере значение переменной m будете выведено в поле Label. В таблице ниже Вам будут представлены основные функции преобразования Delphi:

Применение функций Delphi:

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

Примеры.

Структура функции Delphi

Как организована инструкция функции в языке Delphi? В любом языке программирования на первом этапе описания функции указывается ее заголовок. Далее за заголовком программист описывает раздел объявления констант const (если таковы имеются), затем занимается описанием раздела объявления типов type, далее следует раздел объявления переменных var и, наконец, раздел инструкций.

В приведенном примере в заголовке функции вначале указывается зарезервированное слово function, а следом идет имя функции. Далее в скобках программист перечисляет список параметров, и вслед за ним, используя символ «:», указывает тип значения функции. В конце каждого заголовка стоит символ «;». После заголовка следуют раздел констант, раздел типов, раздел переменных. Внутри раздела инструкций кроме констант и переменных, описанных соответственно в разделах const и var, может находится переменная result.

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

Round — Функция Delphi

Доброго времени суток!
Есть такая проблема:
Делаю

SetRoundMode(rmNearest);
RoundTo(123.345, -2)

причем, если передаю 1.245, то выдает 1.25

Может дело в том, что 123.345 он понимает как 123.3449999999999 и делает потом Trunc?
Как нормально округлить число? Писать свою функцию с использованием Round?

От: mlecter
Дата: 15.09.04 14:37
Оценка:
От: Diouzshev
Дата: 15.09.04 15:05
Оценка:

Hello, mlecter!
You wrote on Wed, 15 Sep 2004 13:57:07 GMT:

m> Делаю

m> SetRoundMode(rmNearest);
m> RoundTo(123.345, -2)

m> Получаю

m> 123.34

m> причем, если передаю 1.245, то выдает 1.25

m> Может дело в том, что 123.345 он понимает как 123.3449999999999 и
m> делает потом Trunc?
m> Как нормально округлить число? Писать свою функцию с использованием
m> Round?

Ого-го. Вот это да! Глюк у RoundTo на таких маленьких числах!
Помогло изменение функции RoundTo:
function RoundTo(const AValue: Extended <было Double>; const ADigit: TRoundToRange): Extended; <было Double>
var
LFactor: Extended; <было Double>
begin
LFactor := IntPower(10, ADigit);
Result := Round(AValue / LFactor) * LFactor;
end;

После изменений 1.245 стало округлять правильно — до 1.24 %)

Народ! У кого D7 скажите, этот глюк сохранился?

With best regards, Alexander Diouzshev-Maltsev.

От: Diouzshev
Дата: 15.09.04 15:07
Оценка:

Hello, mlecter!
You wrote on Wed, 15 Sep 2004 14:37:23 GMT:

m> Проблема в RoundMode
m> Если даже стоит rmNearest округление происходит банковским методом.
m> Можно ли округлять не банковским методом?
Можно!
Ручками. Но зачем?

With best regards, Alexander Diouzshev-Maltsev.

От: AMogil
Дата: 15.09.04 15:09
Оценка:

Здравствуйте, mlecter, Вы писали:

M>Доброго времени суток!
M>Есть такая проблема:
M>Делаю

M>SetRoundMode(rmNearest);
M>RoundTo(123.345, -2)

M>Получаю

M>123.34

M>причем, если передаю 1.245, то выдает 1.25

M>Может дело в том, что 123.345 он понимает как 123.3449999999999 и делает потом Trunc?
M>Как нормально округлить число? Писать свою функцию с использованием Round?

От: AMogil
Дата: 15.09.04 15:10
Оценка:

Здравствуйте, AMogil, Вы писали:

AM>Здравствуйте, mlecter, Вы писали:

M>>Доброго времени суток!
M>>Есть такая проблема:
M>>Делаю

M>>SetRoundMode(rmNearest);
M>>RoundTo(123.345, -2)

M>>Получаю

M>>123.34

M>>причем, если передаю 1.245, то выдает 1.25

M>>Может дело в том, что 123.345 он понимает как 123.3449999999999 и делает потом Trunc?
M>>Как нормально округлить число? Писать свою функцию с использованием Round?

AM>SimpleRoundTo?

AM>Алексей.

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

От: mlecter
Дата: 15.09.04 15:16
Оценка:

Здравствуйте, AMogil, Вы писали:

AM>>Алексей.

AM>Она округляет до большего, т.е. для положительных чисел все делает как мы привыкли.

AM>Алексей.

Не до большего, а до ближайшего четного.
1.345 -> 1.34
1.355 -> 1.36

«Мы» так не привыкли

От: mlecter
Дата: 15.09.04 15:19
Оценка:

Здравствуйте, AMogil, Вы писали:

AM>SimpleRoundTo?

AM>Алексей.

SimpleRoundTo(123.345, -2) = 123.34
RoundMode = rmNearest

От: AMogil
Дата: 15.09.04 15:31
Оценка:

Здравствуйте, mlecter, Вы писали:

M>Здравствуйте, AMogil, Вы писали:

AM>>>Алексей.

AM>>Она округляет до большего, т.е. для положительных чисел все делает как мы привыкли.

AM>>Алексей.

M>Не до большего, а до ближайшего четного.
M>1.345 -> 1.34
M>1.355 -> 1.36

M>»Мы» так не привыкли

Пример из справки Delphi

SimpleRoundTo(1234567, 3) 1234000
SimpleRoundTo(1.234, -2) 1.23 .
SimpleRoundTo(1.235, -2) 1.24
SimpleRoundTo(-1.235, -2) -1.23

SimpleRoundTo uses asymmetric arithmetic rounding to determine how to round values that are exactly m >

От: AMogil
Дата: 15.09.04 15:36
Оценка: 2 (1)

Здравствуйте, AMogil, Вы писали:

AM>Здравствуйте, mlecter, Вы писали:

M>>Здравствуйте, AMogil, Вы писали:

AM>>>>Алексей.

AM>>>Она округляет до большего, т.е. для положительных чисел все делает как мы привыкли.

AM>>>Алексей.

M>>Не до большего, а до ближайшего четного.
M>>1.345 -> 1.34
M>>1.355 -> 1.36

M>>»Мы» так не привыкли

AM>Пример из справки Delphi

AM>Expression Value

AM>SimpleRoundTo(1234567, 3) 1234000
AM>SimpleRoundTo(1.234, -2) 1.23 .
AM>SimpleRoundTo(1.235, -2) 1.24
AM>SimpleRoundTo(-1.235, -2) -1.23

AM>И техт:

AM>SimpleRoundTo uses asymmetric arithmetic rounding to determine how to round values that are exactly midway between the two values that have the desired number of significant digits. This method always rounds to the larger value.

От: mlecter
Дата: 15.09.04 15:53
Оценка:

Здравствуйте, AMogil, Вы писали:

AM>>Пример из справки Delphi

AM>>Expression Value

AM>>SimpleRoundTo(1234567, 3) 1234000
AM>>SimpleRoundTo(1.234, -2) 1.23 .
AM>>SimpleRoundTo(1.235, -2) 1.24
AM>>SimpleRoundTo(-1.235, -2) -1.23

AM>>И техт:

AM>>SimpleRoundTo uses asymmetric arithmetic rounding to determine how to round values that are exactly midway between the two values that have the desired number of significant digits. This method always rounds to the larger value.

AM>Попробуйте

AM>SetRoundMode(rmDown);

Спасибо, заработало
Хотя странно, SimpleRoundTo вроде не должен использовать RoundMode

От: mlecter
Дата: 15.09.04 16:04
Оценка:

Здравствуйте, mlecter, Вы писали:

M>Здравствуйте, AMogil, Вы писали:

AM>>>Пример из справки Delphi

AM>>>Expression Value

AM>>>SimpleRoundTo(1234567, 3) 1234000
AM>>>SimpleRoundTo(1.234, -2) 1.23 .
AM>>>SimpleRoundTo(1.235, -2) 1.24
AM>>>SimpleRoundTo(-1.235, -2) -1.23

AM>>>И техт:

AM>>>SimpleRoundTo uses asymmetric arithmetic rounding to determine how to round values that are exactly midway between the two values that have the desired number of significant digits. This method always rounds to the larger value.

AM>>Попробуйте

AM>>SetRoundMode(rmDown);

M>Спасибо, заработало
M>Хотя странно, SimpleRoundTo вроде не должен использовать RoundMode

M>

M>В чем причина?

Тем более, что после того как я сделаю SetRoundMode(rmDown), RoundTo начинает все округлять (естественно) вниз, в то время как SimpleRoundTo начинает округлять нормально. Получается путанница.

От: AMogil
Дата: 15.09.04 16:13
Оценка:

Здравствуйте, mlecter, Вы писали:

M>Здравствуйте, AMogil, Вы писали:
M>В чем причина?

Интересный фокус:
При вычислении SimpleRoundTo(1.345 , -2) по информации watch’a:

RoundMode rmUp rmDown

В исходном коде SimpleRoundTo
AValue / LFactor 134.5 134.5
(AValue / LFactor) + 0.5 135 135
Trunc((AValue / LFactor) + 0.5) 134 (. ) 135

В моем коде (Trunc(135)) 135 135

От: AMogil
Дата: 15.09.04 16:17
Оценка:

Здравствуйте, AMogil, Вы писали:

AM>Здравствуйте, mlecter, Вы писали:

Форматирование сбилось. Снова табличка.

От: Аноним
Дата: 15.09.04 18:47
Оценка:

Здравствуйте, AMogil, Вы писали:

AM>

Да, дело в IntPower.
rmNearest
lFactor := IntPower(10, -2) > 0.01 !
Frac((AValue / LFactor) + 0.5) = 1 .
отсюда и Trunc(. ) = 134

rmDown
lFactor := IntPower(10, -2) Re[3]: RoundTo

От: kavlad http://www.wavesoft.ru
Дата: 16.09.04 06:05
Оценка:

Здравствуйте, Diouzshev, Вы писали:

m>> Проблема в RoundMode
m>> Если даже стоит rmNearest округление происходит банковским методом.
m>> Можно ли округлять не банковским методом?
D>Можно!

В семерке есть функия

SimpleRoundTo function
Rounds a floating-point value to a specified digit or power of ten using asymmetric arithmetic rounding.

От: mlecter
Дата: 16.09.04 08:03
Оценка:

В итоге рабочая процедура выглядит так:

Через одно место, зато работает всегда, без всяких rmDown.

Round — Функция 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:
Вот давай разберемся с твоим кодом. Для начала приведем описания стандартных функций Делфи:

function Int(X: Extended): Extended;
function Round(X: Extended): Int64;
function Trunc(X: Extended): Int64;

Отсюда мы видим почему тебе пришлось использовать нагромождение

Все совершенно правильно заметили, что оно явно избыточное. Но если мы уберем функцию round, то получим несовместимость типов переменной des1 и результата выражения справа в операторе присваивания. Т.е.
Integer = Extended. Конечно же Делфи с этим ни за что не согласится, поэтому и появилась функция round, которая в данном случае просто обеспечивает явное преобразование типов.
На самом деле все очень просто разрешается с использованием функции Trunc. Она как и Int просто отбрасывает дробную часть, но результат при этом будет целого типа, что тебе и нужно в данном случае. Следовательно, должно быть так:

И совершенно прав Paul, когда говорит об ошибках представления чисел в ПК, обусловленных конечностью разрядной сетки и некоторыми другими факторами. Это не нонсенс, это вполне реально, когда 2,0 * 2,0 = 3,99999999999999999999999999. Ну может чуть утрированно :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

Илон Маск рекомендует:  Знакомство с гридами
Понравилась статья? Поделиться с друзьями:
Кодинг, CSS и SQL