2009-03-15 2 views
4

Когда мы конвертируем float в integer в visual basic 6.0, как он округляет дробную часть? Я говорю о автоматическом преобразовании типов.Автоматическое преобразование типов в Visual Basic 6.0

Если мы относим как

Dim i as Integer 
i=5.5 
msgbox i 

Что он будет печатать? 5 или 6?

Я получал «5» пару месяцев назад. Однажды это начало давать мне 6! Любая идея, что не так? Microsoft выпустила некоторые исправления, чтобы что-то исправить?

Обновление: 5.5 преобразуется в 6, но от 8,5 до 8!

Обновление 2: Добавление CInt не имеет значения. CInt (5.5) дает 6, а Cint (8.5) дает 8 !! Собственное поведение. Я должен попробовать что-то наподобие пола (x + 0,49);

+0

Стоп говоря "бросок". Это не классификация типов, это преобразование типов. Существует большая разница. – Bob

+0

Мой плохой. Спасибо за исправление броска – Tuxist

+0

Tuxist, поэтому вы должны отредактировать свой вопрос и изменить «cast» и «casting» на «convert» и «conversion». – MarkJ

ответ

7

Часть этого в VB6 помощь: Тема Функции Тип преобразования. К сожалению, это одна из тем, которая находится не в VB6 documentation на веб-сайте MSDN, но если вы установили справку с VB6, она будет там.

Когда дробная часть ровно 0,5, CInt и CLng всегда округлить до ближайшего четного числа. Так, например, 0,5 округляется до 0, и 1,5 округляется до 2. CInt и CLng отличаются от Fix и Int функции, которые усечение, а не круглое, дробная часть числа. Кроме того, Fix и Int всегда возвращают значение того же типа, что и передано.

Неявные тип принуждения - а.к.а. "evil type coercion (PDF)" - из числа с плавающей точкой к целому числу использует те же правила округления, как CInt и CLng. Такое поведение, похоже, не документировано нигде в руководстве.

Если вы хотите, чтобы округлить когда дробная часть> = 0,5, и вниз в противном случае, простой способ сделать это является

n = Int(x + 0.5) 

И с верхней части моей голову, вот мой краток версия Mike Spross RoundNumber :), которая является заменой функции VB6 Round.

'Written off the top of my head, seems to work. 
Public Function RoundNumber(ByVal value As Double, Optional PlacesAfterDecimal As Integer = 0) As Double 
    Dim nMultiplier As Long 
    nMultiplier = 10^PlacesAfterDecimal 
    RoundNumber = Int(0.5 + value/nMultiplier) * CDbl(nMultiplier) 
End Function 

Пример вывода:

Debug.Print RoundNumber(1.6)  '2' 
Debug.Print RoundNumber(-4.8)  '-5' 
Debug.Print RoundNumber(101.7)  '102' 
Debug.Print RoundNumber(12.535, 2) '12.54' 
+0

В 'RoundNumber' сначала нужно умножить, затем разделите:' RoundNumber = Int (0.5 + value * nMultiplier)/CDbl (nMultiplier) 'не наоборот. Самая смешная часть - функция '' 'фактически возвращает 'Двойной' в этом (retval - тот же тип, что и параметр), поэтому он работает и для больших чисел/больших значений. Вероятно, 'CDec (0.5) + ...' также значительно повысит точность. – wqw

1

Поврежденное поведение звучит тревожно, однако правильный ответ: 6. Прокрутите вниз до «Круглый до четного метода» на Wikipedia, Rounding для объяснения.

7

Update: Через некоторое Googling, я наткнулся на the following article:

Это не "ошибка", это путь VB был предназначен для работы. Он использует что-то известное как округление Banker, который, если числа оканчивается ровно 5, и вы хотите округлить до позиции перед из 5, она округляет число вниз, если числа перед 5'в положения является даже и округляет в противном случае. Предполагается защитить от повторных вычислений, используя округленные числа , так что ответ не всегда смещен вверх. Подробнее об этом вопросе, чем вы, вероятно, хотите знать, видеть эту ссылку

http://support.microsoft.com/default.aspx?scid=KB;EN-US;Q196652

Это объясняет (видимое) странное поведение:

Cint(5.5) 'Should be 6' 
Cint(8.5) 'Should be 8' 

Старого Обновление: Возможно, вы должны быть более явными: используйте CInt, вместо того, чтобы просто присваивать float целому числу. Например:

Dim i as Integer 
i = CInt(5.5) 
MsgBox i 
+0

Не имеет значения. CInt (5.5) дает 6, а Cint (8.5) дает 8 !! Собственное поведение. Я должен попробовать что-то наподобие пола (x + 0,49); – Tuxist

+0

Хорошо, это _is_ странно. – elo80ka

+0

Это нормально. Функции преобразования типа (CInt, CLng и т. Д.) Округляются до преобразования. –

0

Функция VB6 Круглый() использует метод Banker's Rounding. MS KB Article 225330 (http://support.microsoft.com/kb/225330) говорит об этом косвенно путем сравнения VBA в Office 2000 для нативного поведения Excel и описывает это следующим образом:

Когда число четное целое число заканчивается в .5, Visual Basic округляет число (вниз) до ближайшего целого числа. [...] Эта разница [между VBA и Excel] равна только для чисел, оканчивающихся на .5 и одинаковых с другими дробными числами.

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

1

Как уже отмечалось, «странное поведение», которое вы видите, связано с тем, что при округлении дробных значений VB6 использует Banker's Rounding.

Обновление 2: Добавление CInt не делает разница. CInt (5.5) дает 6 и Cint (8.5) дает 8 !!

Это также нормально. CInt всегда совершает раунды (снова используя метод округления Banker) перед выполнением преобразования.

Если у вас есть номер с дробной частью и просто хотите обрезать его (игнорировать часть после десятичной точки), вы можете использовать либо Fix или Int функции:

Fix(1.5) = 1 
Fix(300.4) = 300 
Fix(-12.394) = -12 

Int работает Точно так же, как Fix, за исключением того, что она округляет отрицательные числа к следующему-самому низкому отрицательному числу, за исключением:

Int(1.5) = 1 
Int(300.4) = 300 
Int(-12.394) = -13 

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


EDIT: См MarkJ's answer для гораздо проще (и, вероятно, быстрее) версии этой функции.


' Rounds value to the specified number of places' 
' Probably could be optimized. I just wrote it off the top of my head,' 
' but it seems to work.' 
Public Function RoundNumber(ByVal value As Double, Optional PlacesAfterDecimal As Integer = 0) As Double 

    Dim expandedValue As Double 
    Dim returnValue As Double 
    Dim bRoundUp As Boolean 

    expandedValue = value 
    expandedValue = expandedValue * 10^(PlacesAfterDecimal + 1) 
    expandedValue = Fix(expandedValue) 

    bRoundUp = (Abs(expandedValue) Mod 10) >= 5 

    If bRoundUp Then 
     expandedValue = (Fix(expandedValue/10) + Sgn(value)) * 10 
    Else 
     expandedValue = Fix(expandedValue/10) * 10 
    End If 

    returnValue = expandedValue/10^(PlacesAfterDecimal + 1) 
    RoundNumber = returnValue 

End Function 

Примеры

Debug.Print RoundNumber(1.6)  '2' 
Debug.Print RoundNumber(-4.8)  '-5' 
Debug.Print RoundNumber(101.7)  '102' 
Debug.Print RoundNumber(12.535, 2) '12.54' 
+0

Это более кратковременно и, вероятно, работает быстрее, чтобы использовать Int (0.5 + value) для округления до ближайшего целого числа. И посмотрите мой ответ на мою версию функции RoundNumber. BTW RoundNumber (-4.8) предположительно должен быть -5 не 5 – MarkJ

+0

@MarkJ: Да, это определенно проще. Я продолжал думать: «Вау, должен быть очевидный и простой способ сделать это» :-) Я отнесю OP к вашему ответу и +1 за вас, добрый сэр. –

+0

Приветствия! Кто-то показал мне этот трюк, гм, я считаю ZX Spectrum BASIC, когда-то около 20 лет назад ... – MarkJ