Тип данных Double - Ошибка в вычислениях.

Программирование на Visual Basic for Applications
Wasup!
Продвинутый пользователь
Продвинутый пользователь
 
Сообщения: 120
Зарегистрирован: 21.06.2005 (Вт) 11:09

Тип данных Double - Ошибка в вычислениях.

Сообщение Wasup! » 08.11.2005 (Вт) 11:31

При выполнении следующего кода, получаются различные результаты для различных числовых типов данных Double и Currency

Код: Выделить всё
Sub dbl()
Dim a As Double
Dim b As Double
Dim c As Double

Dim a2 As Currency
Dim b2 As Currency
Dim c2 As Currency

Dim a3, b3, c3
c3 = CDec(0)

a = 20226827.81
b = 20226827.01

a2 = 20226827.81
b2 = 20226827.01


a3 = CDec(20226827.81)
b3 = CDec(20226827.01)
c3 = CDec(0)


c = a - b
c2 = a2 - b2
c3 = a3 - b3
Debug.Print "c=" & c, "c2=" & c2, "c3=" & c3


End Sub


c=0,799999997019768 c2=0,8 c3=0,8

Непонятно, как получился такой странный результат с :?: Ведь верным ответом должно быть 0.8?
Win2000, Excel 97 и 2000

Может быть я не замечаю очевидного?
Для каких чисел, какой тип данных (Single,Double,Currency) следует использовать?

tyomitch
Пользователь #1352
Пользователь #1352
Аватара пользователя
 
Сообщения: 12822
Зарегистрирован: 20.10.2002 (Вс) 17:02
Откуда: חיפה

Сообщение tyomitch » 08.11.2005 (Вт) 12:29

1) Память компьютера конечна, поэтому он округляет результат.
2) Память компьютера состоит из двоичных битов, поэтому он округляет результат до степеней двойки (а не десятки, как тебе привычнее).

Если тебе нужен результат с точностью до 1 знака после запятой, то так явно и напиши: c = Round(c, 1)
Изображение

alibek
Большой Человек
Большой Человек
 
Сообщения: 14205
Зарегистрирован: 19.04.2002 (Пт) 11:40
Откуда: Russia

Сообщение alibek » 08.11.2005 (Вт) 12:35

Currency -- это число с фиксированной запятой (четыре позиции после запятой). Если в твоих вычислениях не требуется большая точность, то используй его. Оно специально предназначено для работы с денежными суммами.
Lasciate ogni speranza, voi ch'entrate.

Wasup!
Продвинутый пользователь
Продвинутый пользователь
 
Сообщения: 120
Зарегистрирован: 21.06.2005 (Вт) 11:09

Сообщение Wasup! » 08.11.2005 (Вт) 12:53

Дело не в получении округленного результата, а в том, что результат неверный:
20226827.81
-
20226827.01
=
0.8
и никакого округления при этом возникать не должно.
А при использовании типа Double результат 0.7999.. т.е. происходит не округление, а наоборот

alibek
Большой Человек
Большой Человек
 
Сообщения: 14205
Зарегистрирован: 19.04.2002 (Пт) 11:40
Откуда: Russia

Сообщение alibek » 08.11.2005 (Вт) 13:13

Перечитай tyomitchа, особенно пункт 2.
Повторяй, пока не поймешь.
Lasciate ogni speranza, voi ch'entrate.

Wasup!
Продвинутый пользователь
Продвинутый пользователь
 
Сообщения: 120
Зарегистрирован: 21.06.2005 (Вт) 11:09

Сообщение Wasup! » 08.11.2005 (Вт) 22:48

Не помогает :oops: :oops: :oops: можно на примере?
Ну не понимаю я, почему Excel ошибается а win калькулятор нет?
И что единственный выход вставлять round везде, где только можно?
Код: Выделить всё

Sub dbl()
Dim a As Double
Dim b As Double
Dim c As Double
Dim x As Double
Dim y As Double
Dim z As Double


a = 123456789.81
b = 123456789.01
c = a - b 'результат  0,799999997019768

x = 0.12345678981
y = 0.67654321019
z = x + y 'результат 0,8

If c = z Then
    Debug.Print "c = z"
Else
    Debug.Print "c <> z"
End If
End Sub


Результат
c <> z, а должно быть c=z :?:

tyomitch
Пользователь #1352
Пользователь #1352
Аватара пользователя
 
Сообщения: 12822
Зарегистрирован: 20.10.2002 (Вс) 17:02
Откуда: חיפה

Сообщение tyomitch » 08.11.2005 (Вт) 22:56

Числа с плавающей точкой нельзя сравнивать на равенство.
Нам это рассказывали на самом первом занятии по программированию в школе.
Нужно ввести некоторую погрешность, скажем "Const eps = 1e-6", и проверять так: "If Abs(x-y) < eps"

Ну пойми ты, что точных вычислений с плавающей точкой в компьютере не бывает.
Изображение

alibek
Большой Человек
Большой Человек
 
Сообщения: 14205
Зарегистрирован: 19.04.2002 (Пт) 11:40
Откуда: Russia

Сообщение alibek » 09.11.2005 (Ср) 8:46

Wasup! писал(а):Не помогает :oops: :oops: :oops: можно на примере?
Ну не понимаю я, почему Excel ошибается а win калькулятор нет?
И что единственный выход вставлять round везде, где только можно?

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

Почитай о том, как десятичные числа отображаются в двоичном представлении. Особенно вещественные (дробные) числа. Тогда поймешь, почему 0.125 будет вычисляться правильно, а 0.12 нет.
Lasciate ogni speranza, voi ch'entrate.

alibek
Большой Человек
Большой Человек
 
Сообщения: 14205
Зарегистрирован: 19.04.2002 (Пт) 11:40
Откуда: Russia

Сообщение alibek » 09.11.2005 (Ср) 8:47

tyomitch писал(а):Ну пойми ты, что точных вычислений с плавающей точкой в компьютере не бывает.

Не совсем так :)
Смотря чему равна дробная часть.
Lasciate ogni speranza, voi ch'entrate.

tyomitch
Пользователь #1352
Пользователь #1352
Аватара пользователя
 
Сообщения: 12822
Зарегистрирован: 20.10.2002 (Вс) 17:02
Откуда: חיפה

Сообщение tyomitch » 09.11.2005 (Ср) 9:43

alibek писал(а):
tyomitch писал(а):Ну пойми ты, что точных вычислений с плавающей точкой в компьютере не бывает.

Не совсем так :)
Смотря чему равна дробная часть.

"даже стоящие часы дважды в сутки показывают правильное время" (c)
;-)
Изображение

Al Khamid
Бывалый
Бывалый
Аватара пользователя
 
Сообщения: 274
Зарегистрирован: 11.02.2004 (Ср) 10:00
Откуда: Москва, Ховрино

Сообщение Al Khamid » 09.11.2005 (Ср) 14:39

Последний раз редактировалось Al Khamid 24.11.2007 (Сб) 12:58, всего редактировалось 1 раз.

Wasup!
Продвинутый пользователь
Продвинутый пользователь
 
Сообщения: 120
Зарегистрирован: 21.06.2005 (Вт) 11:09

Сообщение Wasup! » 11.11.2005 (Пт) 9:41

Интересная информация по теме:
http://www.sql.ru/forum/actualthread.aspx?bid=4&tid=160294&hl=decimal
http://www.sql.ru/forum/actualthread.aspx?bid=4&tid=31080
http://www.sql.ru/forum/actualthread.aspx?bid=1&tid=8740

Только вот все это меня окончательно запутало, как все-таки лучше хранить числа? Рекомендации самые разные..

Главный вопрос гарантирует ли мне тип currency правильность расчетов и округления? И в каких случаях?

И как лучше поступать: округлять после каждого вычисления до 2-х знаков, или только при выдаче результатов?

Кстати, как я выяснил, Excel при выводе числа в ячейку неявно преобразует числа в Double, так что при больших числах точности Currency на рабочем листе уже не добиться.. :(

Как быть если нужно использовать большую точность, чем 4 знака после зпт? Или если число больше 15 знаков? Использовать Double или Decimal?

И последний вопрос в Excel97 нет ф-ции Round( ) (или я просто плохо искал?). Как лучше писать свою или округлять с помощью Format(123.00005, "#.####")?

alibek
Большой Человек
Большой Человек
 
Сообщения: 14205
Зарегистрирован: 19.04.2002 (Пт) 11:40
Откуда: Russia

Сообщение alibek » 11.11.2005 (Пт) 10:24

В VB ты не сможешь пользоваться Decimal.
Сам подумай, что тебе надо, и решай чем будешь пользоваться.

Числа с фиксированной запятой (Integer, Long, Currency, а также всякие Decimal), гарантируют тебе точность в пределах своей разрядности.
Числа с плавающей запятой карантируют только точность в пределах определенного числа значащих разрядов. Если у тебя маленькое значение (единицы, десятки), то Double тебе гарантирует примерно 9 знаков после запятой, т.е. миллиардные доли. Если у тебя значения порядка миллионов, то будет обрезаться все, что лежит за 6-7 знаком после запятой, т.е. миллионные доли еще будут, а миллиардные будут сбрасываться в 0. Если у тебя значения порядка 1e12-1e14, то на дробные доли можешь вообще не рассчитывать.
Lasciate ogni speranza, voi ch'entrate.

Wasup!
Продвинутый пользователь
Продвинутый пользователь
 
Сообщения: 120
Зарегистрирован: 21.06.2005 (Вт) 11:09

Сообщение Wasup! » 14.11.2005 (Пн) 9:57

В VB ты не сможешь пользоваться Decimal.

А CDec( ) - ?

Функцию Round в 97 Excel нашел, Application.WorksheetFunction.Round(Arg1 as Double, Arg2 as Double) as Double, но ее поведение еще не успел изучить..

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

alibek
Большой Человек
Большой Человек
 
Сообщения: 14205
Зарегистрирован: 19.04.2002 (Пт) 11:40
Откуда: Russia

Сообщение alibek » 14.11.2005 (Пн) 10:04

Wasup! писал(а):А CDec( ) - ?

И что? В какую переменную ты его вернешь?

Для денежным сумм давно придумали Currency, вот его и надо использовать.
Lasciate ogni speranza, voi ch'entrate.

Wasup!
Продвинутый пользователь
Продвинутый пользователь
 
Сообщения: 120
Зарегистрирован: 21.06.2005 (Вт) 11:09

Сообщение Wasup! » 14.11.2005 (Пн) 18:14

вернуть можно в variant.. Но дальше возникает реальная проблема, на рабочий лист можно вернуть только Double или Cтроку! Причем то же самое происходит и для Currency


Код: Выделить всё

Sub abc()
    Dim a, b, c
    Dim x As Currency, y As Currency, z As Currency
   
    a = CDec("12345678901234567890,12345678")
    b = CDec("12345678901234567890,12345678")
    c = a + b
    Cells(1, 1) = "Decimal"
    Cells(2, 1) = c
    Cells(3, 1) = "'" & CStr(c)

    x = CCur("12345678901245,1234")
    y = CCur("12345678901245,1234")
    z = x + y
    Cells(4, 1) = "Currency"
    Cells(5, 1) = z
    Cells(6, 1) = "'" & CStr(z)
End Sub

Результаты будут

Decimal
24691357802469100000,00
24691357802469135780,24691356
Currency
24691357802490,2000
24691357802490,2468

как быть :?:

dvr
Начинающий
Начинающий
 
Сообщения: 1
Зарегистрирован: 19.11.2005 (Сб) 12:14

Сообщение dvr » 19.11.2005 (Сб) 12:22

Столкнулся с тойже проблемой, типы Single или Double выдают погрешность, при довольно больших объемах вычислений она очень даже заметна. Нет ли у кого ссылки на описание того, как VBA хранит переменные в памяти и как он выполняет операции над ними побитно? Был бы очень благодарен.

1) Память компьютера конечна, поэтому он округляет результат.
2) Память компьютера состоит из двоичных битов, поэтому он округляет результат до степеней двойки (а не десятки, как тебе привычнее).

ИМХО, результат округляется только если число не укладывается в отведенное ему кол-во байт, в примере же 8 байт на число хватает с избытком.

Wasup!
Продвинутый пользователь
Продвинутый пользователь
 
Сообщения: 120
Зарегистрирован: 21.06.2005 (Вт) 11:09

Сообщение Wasup! » 21.11.2005 (Пн) 14:53

ИМХО: Single и Double это типы данных с плавающей запятой, и погрешность возникает при переводе числа из формы с фиксированной запятой в форму с плавающей..

И как я понял этим страдают не только VBA, но и VB, C/C++ и многие другие.. (или я сильно не прав :cry: :?: )

Еще раз попрошу высказаться уважаемых участников форума по этой проблеме, кто ее как решил или избежал?

alibek
Большой Человек
Большой Человек
 
Сообщения: 14205
Зарегистрирован: 19.04.2002 (Пт) 11:40
Откуда: Russia

Сообщение alibek » 21.11.2005 (Пн) 16:19

Wasup! писал(а):ИМХО: Single и Double это типы данных с плавающей запятой, и погрешность возникает при переводе числа из формы с фиксированной запятой в форму с плавающей..

Нет. Ответ дал tyomitch в первом же своем посте.
Lasciate ogni speranza, voi ch'entrate.

tyomitch
Пользователь #1352
Пользователь #1352
Аватара пользователя
 
Сообщения: 12822
Зарегистрирован: 20.10.2002 (Вс) 17:02
Откуда: חיפה

Сообщение tyomitch » 23.11.2005 (Ср) 7:12

alibek писал(а):
Wasup! писал(а):ИМХО: Single и Double это типы данных с плавающей запятой, и погрешность возникает при переводе числа из формы с фиксированной запятой в форму с плавающей..

Нет. Ответ дал tyomitch в первом же своем посте.

Ещё раз для тех, кто на бронепоезде: погрешность возникает уже при переводе числа в десятичное представление или обратно.

И это проблема не VB или Си, а всех двоичных компьютеров.

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

alibek
Большой Человек
Большой Человек
 
Сообщения: 14205
Зарегистрирован: 19.04.2002 (Пт) 11:40
Откуда: Russia

Сообщение alibek » 23.11.2005 (Ср) 8:33

tyomitch писал(а):"Перевод числа из формы с фиксированной запятой в форму с плавающей" - это просто выставление порядка. Никакой погрешности при этом быть не может.

А вот с этим не соглашусь.
Lasciate ogni speranza, voi ch'entrate.

tyomitch
Пользователь #1352
Пользователь #1352
Аватара пользователя
 
Сообщения: 12822
Зарегистрирован: 20.10.2002 (Вс) 17:02
Откуда: חיפה

Сообщение tyomitch » 23.11.2005 (Ср) 14:58

alibek, ты про фиксированную двоичную точку (которая и есть настоящая фиксированная точка), или про десятичную, которая в Currency?

(потому что плавает-то именно двоичная точка)
Изображение

alibek
Большой Человек
Большой Человек
 
Сообщения: 14205
Зарегистрирован: 19.04.2002 (Пт) 11:40
Откуда: Russia

Сообщение alibek » 23.11.2005 (Ср) 15:23

Я про плавающую запятую, про то, что:
123456789 (Long) = 123456800 (Single)
Lasciate ogni speranza, voi ch'entrate.

tyomitch
Пользователь #1352
Пользователь #1352
Аватара пользователя
 
Сообщения: 12822
Зарегистрирован: 20.10.2002 (Вс) 17:02
Откуда: חיפה

Сообщение tyomitch » 23.11.2005 (Ср) 16:04

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


Вернуться в VBA

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 95

    TopList  
cron