Округление
От: Smetanin Россия  
Дата: 11.10.01 04:18
Оценка:
Возникла проблема какая-то странная на мой взгляд... Ф-ция округления в бейсике работает криво. Решил написать на VC dll-ку с таковой функцией и ничего не вышло... Собственно — не могу придумать корректную ф-цию округления более-менее высокого уровня. Не хотелось лезть во внутреннее представление числа и самому его разбирать...
Поскольку проблема достаточно очевидная, надеюсь кто-то сможет подсказать. Я уже отчаялся сделать ф-цию округления до произвольного количества знаков, нужно хотя бы до двух (имеет место бухг. система).
Есть надёжный вариант — взять число в виде строки и так его округлить (строкой же и вернуть), но хотелось сделать побыстрее.
May the source be with you!
Re: Округление
От: Snax Россия  
Дата: 11.10.01 04:30
Оценка:
Здравствуйте Smetanin, Вы писали:

S>Возникла проблема какая-то странная на мой взгляд... Ф-ция округления в бейсике работает криво


А в чем кривость-то? И как должно быть правильно?
Re[2]: Округление
От: Smetanin Россия  
Дата: 11.10.01 04:34
Оценка:
Здравствуйте Snax, Вы писали:

S>>Ф-ция округления в бейсике работает криво


S>А в чем кривость-то? И как должно быть правильно?


Private Sub Command1_Click()
Dim i As Double
i = 0.005
MsgBox "Round(i,2) = " & CStr(Round(i, 2))
End Sub

Должно получаться, IMHO 0.01, а получается 0. Не знаю где как, а наших бухгалтеров это не устраивает :-)
May the source be with you!
Re[3]: Округление
От: migel  
Дата: 11.10.01 04:45
Оценка:
Здравствуйте Smetanin, Вы писали:

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


S>Private Sub Command1_Click()

S> Dim i As Double
S> i = 0.005
S> MsgBox "Round(i,2) = " & CStr(Round(i, 2))
S>End Sub

S>Должно получаться, IMHO 0.01, а получается 0.


На С просто:
double Round(double val, int precision)
{
double dFr ;
// увеличиваем (уменьшаем на точность).
val = val * pow(10, precision) ;
// получаем значение до дес. точки.
dFr = modf( val, &val ) ;
// округляем по общему принципу.
if( fabs(dFr) >= 0.5 )
val = val + (dFr > 0.0 ? 1.0 : -1.0 ) ;
// возвращаем значение обратно.
val = val * pow(10,-precision) ;
return val ;
}
Вариант не самый оптимальный по скорости но для бухгалтерии сойдет...
Re[3]: Округление
От: al Россия  
Дата: 11.10.01 04:55
Оценка:
S>Должно получаться, IMHO 0.01, а получается 0. Не знаю где как, а наших бухгалтеров это не устраивает :-)

Зачем мучить C++?
В MSDN есть статейка: HOWTO: Implement Custom Rounding Procedures (Q196652) — почитайте.

Вот Ваша функция:

Option Explicit

Function SymArith(ByVal X As Double, _
Optional ByVal Factor As Double = 1) As Double
SymArith = Fix(X * Factor + 0.5 * Sgn(X)) / Factor
' Alternately:
' SymArith = Abs(AsymArith(X, Factor)) * Sgn(X)
End Function

Private Sub Form_Load()
MsgBox SymArith(0.005, 100)
End Sub


Re[4]: Округление
От: Smetanin Россия  
Дата: 11.10.01 05:08
Оценка:
Здравствуйте al, Вы писали:


S>>Должно получаться, IMHO 0.01, а получается 0. Не знаю где как, а наших бухгалтеров это не устраивает :-)


al>Зачем мучить C++?

al>В MSDN есть статейка: HOWTO: Implement Custom Rounding Procedures (Q196652) — почитайте.

al>Вот Ваша функция:


al>Option Explicit


al>Function SymArith(ByVal X As Double, _

al> Optional ByVal Factor As Double = 1) As Double
al> SymArith = Fix(X * Factor + 0.5 * Sgn(X)) / Factor
al> ' Alternately:
al> ' SymArith = Abs(AsymArith(X, Factor)) * Sgn(X)
al>End Function

al>Private Sub Form_Load()

al> MsgBox SymArith(0.005, 100)
al>End Sub

А теперь попробуйте значние 25.435. Или мне поверьте — получается 25.43 и хоть ты тресни. Могу ещё глюки найти — это так, наугад... Конечно такой очевидный вариант я пробовал.
Тем не менее — спасибо.
May the source be with you!
Re[5]: Округление
От: al Россия  
Дата: 11.10.01 05:27
Оценка:
Здравствуйте Smetanin, Вы писали:

S>А теперь попробуйте значние 25.435. Или мне поверьте — получается 25.43 и хоть ты тресни. Могу ещё глюки найти — это так, наугад... Конечно такой очевидный вариант я пробовал.

S>Тем не менее — спасибо.

А такой вариант: MsgBox CDbl(Format(25.434, ".##"))


Re[6]: Округление
От: Smetanin Россия  
Дата: 11.10.01 05:34
Оценка:
Здравствуйте al, Вы писали:

al>А такой вариант: MsgBox CDbl(Format(25.434, ".##"))


Сейчас некогда искать конкретную константу, но поверте — формат работает так же криво, как и раунд, только на других значениях. К тому же при использовании строк появляется отдельный геморрой с региональными настройками (с десятичным разделителем кокретно)...
May the source be with you!
Re[4]: Округление
От: Smetanin Россия  
Дата: 11.10.01 05:38
Оценка:
Здравствуйте migel, Вы писали:

M>На С просто:

M>double Round(double val, int precision)
M>{
M> double dFr ;
M> // увеличиваем (уменьшаем на точность).
M> val = val * pow(10, precision) ;
M> // получаем значение до дес. точки.
M> dFr = modf( val, &val ) ;
M> // округляем по общему принципу.
M> if( fabs(dFr) >= 0.5 )
M> val = val + (dFr > 0.0 ? 1.0 : -1.0 ) ;
M> // возвращаем значение обратно.
M> val = val * pow(10,-precision) ;
M> return val ;
M>}
M>Вариант не самый оптимальный по скорости но для бухгалтерии сойдет...

Сейчас С под рукой нет, но я делал подобный вариант на VC6 — тоже глючит (хотя и значительно реже бейсиковых). Берусь за выходные предоставить тест, на котором эта функция глюкнет.
May the source be with you!
Re[7]: Округление
От: al Россия  
Дата: 11.10.01 05:49
Оценка:
Здравствуйте Smetanin, Вы писали:


S> К тому же при использовании строк появляется отдельный геморрой с региональными настройками (с десятичным разделителем кокретно)...


В случае CDbl(Format(...)) проблем с десятичным разделителем нет, т.к. и CDbl и Format используют один и
тотже.
Интересно, на каких значениях глючит Format?


Re[8]: Округление
От: Smetanin Россия  
Дата: 11.10.01 06:52
Оценка:
Здравствуйте al, Вы писали:

al>Интересно, на каких значениях глючит Format?


Не совсем на конкретных значениях, но глючит. Например, мне не нравится, как работает вот такой код:

Option Explicit

Private Sub Form_Load()
Dim d As Double
Dim s As String

s = "D" & vbTab & "Format" & vbTab & "Round"

For d = 1 To 1.1 Step 0.001

If CDbl(Format(d, "0.0000")) = 1.055 Or CDbl(Format(d, "0.0000")) = 1.065 _
Or CDbl(Format(d, "0.0000")) = 1.075 Then

s = s & vbNewLine & Format(d, "0.0000") & vbTab & Format(d, "0.00") & _
vbTab & Round(d, 2)

End If

Next d

MsgBox s

Unload Me
End Sub


А хочется, чтобы выше приведённый фрагмент мог отработать так, как логично от него ожидать (математически, т-скть, безупречно).
May the source be with you!
Re[9]: Округление
От: VladD2 Российская Империя www.nemerle.org
Дата: 13.10.01 21:57
Оценка:
Здравствуйте ALL.

Вот 20 минут мыёживался. :)

Может это заработает как надо?

Function vRound2(ByVal fVar As Double) As Double
    vRound2 = Fix(CVar((fVar + 0.0055555555555) * 100)) / 100
End Function

Sub Test()
    Debug.Print vRound2(0.005)
    Debug.Print vRound2(25.4344444444444)
    Debug.Print vRound2(25.4344444444445)
End Sub
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re: Округление
От: volk  
Дата: 15.10.01 22:00
Оценка:
Здравствуйте Smetanin, Вы писали:

S>Возникла проблема какая-то странная на мой взгляд... Ф-ция округления в бейсике работает криво. Решил написать на VC dll-ку с таковой функцией и ничего не вышло... Собственно — не могу придумать корректную ф-цию округления более-менее высокого уровня. Не хотелось лезть во внутреннее представление числа и самому его разбирать...

S>Поскольку проблема достаточно очевидная, надеюсь кто-то сможет подсказать. Я уже отчаялся сделать ф-цию округления до произвольного количества знаков, нужно хотя бы до двух (имеет место бухг. система).
S>Есть надёжный вариант — взять число в виде строки и так его округлить (строкой же и вернуть), но хотелось сделать побыстрее.

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

для округления к n-му разряду
к n+1-му добавляется 5,
все разряды после n отбрасываются.
Тот, кто желает, но не делает, распространяет чуму.
Re: Округление
От: VVP Россия 67524421
Дата: 16.10.01 06:30
Оценка:
Здравствуйте Smetanin, Вы писали:

S>Возникла проблема какая-то странная на мой взгляд... Ф-ция округления в бейсике работает криво. Решил написать на VC dll-ку с таковой функцией и ничего не вышло... Собственно — не могу придумать корректную ф-цию округления более-менее высокого уровня. Не хотелось лезть во внутреннее представление числа и самому его разбирать...

S>Поскольку проблема достаточно очевидная, надеюсь кто-то сможет подсказать. Я уже отчаялся сделать ф-цию округления до произвольного количества знаков, нужно хотя бы до двух (имеет место бухг. система).
S>Есть надёжный вариант — взять число в виде строки и так его округлить (строкой же и вернуть), но хотелось сделать побыстрее.

Вот вам еще два варианта — округление по математике и округление по "бухгалтерии" (всегда вверх):

#define EEps 0.000000001

double round_number(double number, int pos) // pos — число знаков после запятой, если pos<0, то число знаков до запятой.
{ double base=10., coeff=1.;
int i;

for( i=0; i<pos; i++ ) coeff*=base;

number*=coeff;
number=floor(number+0.5);
number/=coeff;

return number;
}//************************************


double round_upper(double number, int pos) // все просто round_upper(2.123,2)=2.13
{ double base=10., coeff=1.;
int i;

for( i=0; i<pos; i++ ) coeff*=base;

number*=coeff;
// если стоит круглое число то округлять не надо
if( fabs(number-floor(number))>EEps )
number=floor(number+1.0); // instead `ceil`
number/=coeff;

return number;
}//************************************

Проблема округления стояла всегда, и будет стоять, поскольку так работает со-процессор. Разрядность-то ограничена, а представление такое:
1.[мантиса]*e^[степень].
Никогда не бойся браться делать то, что делать не умеешь. Помни, ковчег был построен любителем. Профессионалы построили Титаник...
Re[2]: Округление
От: migel  
Дата: 16.10.01 08:06
Оценка:
Здравствуйте Smetanin, Вы писали:

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


S>>Возникла проблема какая-то странная на мой взгляд... Ф-ция округления в бейсике работает криво.

Скорее всего проверять надо не на точные cовпадения а на отклонения т.е.
if (fabs(dValue — 1.75) < 0.005)
// Ok

где 0.005 взято из соображений
0.5*0.1^precission
Re: Округление
От: ndemia Россия http://ndemia.com
Дата: 17.10.01 21:54
Оценка:
2 ALL:

Вот тут аналогичное тоже обсуждали: http://forum.ixbt.com/ubb/Forum26/HTML/007045.html
Заинтересованным в вопросе посмотреть стОит. (там, правда, про Си, но суть та же)
Re[10]: Округление
От: IT Россия linq2db.com
Дата: 17.10.01 22:31
Оценка:
Здравствуйте VladD2, Вы писали:

VD>Вот 20 минут мыёживался. :)


Ну тогда и от меня 3 цента:

double myround (double op,double pow)
{
    if (pow <= 0.)  return  op;
    if (op > 0  &&  op < pow  ||  op < 0  &&  op > pow) return  0.;
    op = floor(op/pow + 0.5) * pow;
    return  op > 0  &&  op < pow  ||  op < 0  &&  op > pow? 0.: op;
}


Округляет до любого числа, например, можно округлить до 6.34 ;)

Что бы округлить до второго знака:

a = myround(b,0.01);
Если нам не помогут, то мы тоже никого не пощадим.
Re[11]: Округление
От: retalik www.airbandits.com/
Дата: 18.10.01 05:04
Оценка:
Здравствуйте IT, Вы писали:

IT>Ну тогда и от меня 3 цента:


[skip]
Посмеемся вместе. Вот фрагмент реальной программы, который любую цену округляет вверх до 50 или 90 руб (маркетологи чертовы :)

double round(double r)
{
    int i00=int(r)%100;
    double r00=int(r/100)*100;
    if(i00<=50) return r00+50;
    if(i00<=90) return r00+90;
    return r00+150;
}
Успехов,
Виталий.
Re: Округление
От: Аноним  
Дата: 24.10.01 07:53
Оценка:
Здравствуйте Smetanin, Вы писали:

S>Возникла проблема какая-то странная на мой взгляд... Ф-ция округления в бейсике работает криво. Решил написать на VC dll-ку с таковой функцией и ничего не вышло... Собственно — не могу придумать корректную ф-цию округления более-менее высокого уровня. Не хотелось лезть во внутреннее представление числа и самому его разбирать...

S>Поскольку проблема достаточно очевидная, надеюсь кто-то сможет подсказать. Я уже отчаялся сделать ф-цию округления до произвольного количества знаков, нужно хотя бы до двух (имеет место бухг. система).
S>Есть надёжный вариант — взять число в виде строки и так его округлить (строкой же и вернуть), но хотелось сделать побыстрее.

Подобная проблема возникает при установке параметров оптимизации на "скорость работы".
Для решения проблемы надо изменить тип оптимизации и проверить, что не помечен пункт относящийся к округлению. К сожалению под рукой сейчас нет компилятора и точное название этого пункта сказать не могу, но тем кто знаком с english проблемы не будет.

IAZ
Кто ищет, тот всегда найдет.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.