Форум программистов, компьютерный форум, киберфорум
VBA
Войти
Регистрация
Восстановить пароль
Карта форума Темы раздела Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.54/13: Рейтинг темы: голосов - 13, средняя оценка - 4.54
381 / 4 / 3
Регистрация: 20.03.2013
Сообщений: 43
1

Сравнение встроенных и математических функций с заданной точностью

02.10.2013, 13:25. Показов 2574. Ответов 8
Метки нет (Все метки)

Author24 — интернет-сервис помощи студентам
Не смогла понять, как это реализовать, поэтому обращаюсь к вам за помощью.

Нужно сравнить значение встроенной функции VBA с ее математическим аналогом и вычислить разницу между встроенной функцией и математическим аналогом, вычисленным с точностью eps. Иными словами, нужно узнать с точностью до eps величину погрешности, с которой VBA вычисляет математическую функцию.

Мой вариант задания - вычислить разницу:

https://www.cyberforum.ru/cgi-bin/latex.cgi?\begin{vmatrix}\mathbb{Sqr(2)}-\sqrt{2}\end{vmatrix}

где Sqr(2) - встроенная функция VBA Sqr(), а √2 - математический корень.
Разницу вычислить с точностью eps = 1E-56.

Помогите, буду признательна!
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
02.10.2013, 13:25
Ответы с готовыми решениями:

Использование математических функций и функций работы со строками
Составить процедуру для вычисления и печати значений переменных y и z для заданных значений...

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

Нужно посчитать количество слов без встроенных функций
Нужно посчитать количество слов без встроенных функций.

Подпрограмма: Вывести имена и значения всех встроенных и пользовательских свойств заданной рабочей книги...
(Exel | Word) Создайте процедуру, которая в таблицу Excel выводит имена и значения всех встроенных...

8
1007 / 351 / 59
Регистрация: 28.02.2013
Сообщений: 932
02.10.2013, 14:03 2
Цитата Сообщение от Але-Дашкова Посмотреть сообщение
с ее математическим аналогом
- это про функцию КОРЕНЬ?
А так в модуль :
Visual Basic
1
2
3
Function Корень_VBA(число)
    Корень_VBA = sqr(число)
End Function
Функции КОРЕНЬ и Корень_VBA мне дали одинаковые результаты.

Добавлено через 1 минуту
1,4142135623731
0
381 / 4 / 3
Регистрация: 20.03.2013
Сообщений: 43
02.10.2013, 14:45  [ТС] 3
Нет, я имела в виду именно математический корень. Но поскольку он, как объяснил преподаватель, представляет собой иррациональное число, то сравнение с математическим корнем достаточно провести с заданной точностью eps.
В моем варианте eps = 1E-56.

Вот пример, который приводил преподаватель:

Задание - вычислить разницу:

https://www.cyberforum.ru/cgi-bin/latex.cgi?\begin{vmatrix}\mathbb{Application.WorksheetFunction.Pi()}-\pi \end{vmatrix}

где Application.WorksheetFunction.Pi() - встроенная в Excel функция для вычисления числа "пи", а https://www.cyberforum.ru/cgi-bin/latex.cgi?\pi - математическое число "пи".
Разницу вычислить с точностью eps = 1E-180.

Ответ:
0,000000000000003238462643383279502884197169399375105820974944592307816406286208 99862803482534211706798214808651328230664709384460955058223172535940812848111745 0284102701938521106
0
1007 / 351 / 59
Регистрация: 28.02.2013
Сообщений: 932
02.10.2013, 16:50 4
пи()=3,14159265358979 в Экселе (это я к тому где взять число Пи с большим числом знаков после запятой).
Вот где найти значение математического корня с таким количеством знаков?
Например:
а - математический корень из 2
1. Вставляете модуль
2. В модуль код (который выше)
3. в листе Экселя (в ячейке):
=abs(Корень_VBA(2)-a)
0
6081 / 1325 / 195
Регистрация: 12.12.2012
Сообщений: 1,023
02.10.2013, 17:03 5
Цитата Сообщение от Михалыч Посмотреть сообщение
Вот где найти значение математического корня с таким количеством знаков?
Очевидно, его надо вычислить в программе. Но как это сделать, я тоже пока не очень соображаю...

Единственный совет, который я могу дать Але-Дашковой сейчас - работать с числами как со строками или с массивами цифр, так как очевидно, что встроенных в VBA числовых типов не хватит для хранения числа с такой точностью.

С уважением,
Aksima
1
здесь больше нет...
3374 / 1672 / 184
Регистрация: 03.02.2010
Сообщений: 1,219
04.10.2013, 13:57 6
Лучший ответ Сообщение было отмечено как решение

Решение

Visual Basic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
Option Explicit
 
Function LongSqr(iNum As Integer, iLen As Integer) As String
    Dim sngT As Single: sngT = Timer
    Dim sRes As String, sRes2 As String
    sRes = CStr(Int(Sqr(iNum)))
    sRes2 = LongMult(2, sRes)
 
    Dim sOst As String, sNum1 As String
    sOst = LongSub(iNum, LongMult(sRes, sRes))
    '*****************
    If sOst = "0" Then
        LongSqr = sRes
        Exit Function
    End If
    '*****************
    sNum1 = sOst & "00"
 
    Dim i As Integer
 
    Do
        For i = 1 To 10
            If isLarger(LongMult(sRes2 & i, i), sNum1) Then
                '==========================================================
                sOst = LongSub(sNum1, LongMult(sRes2 & CStr(i - 1), i - 1))
                sNum1 = sOst & "00"
 
                sRes = sRes & CStr(i - 1)
                sRes2 = LongMult(2, sRes)
                '==========================================================
 
                Exit For
            End If
        Next i
    Loop While Len(sRes) <= iLen
    Debug.Print Timer - sngT
    LongSqr = sRes
End Function
'{
'    Алгоритм извлечения корня квадратного
'
'    Рассмотрим на примере sqrt(273529).
'    Для нахождения произведем следующие действия:
'    1) десятичную запись числа 273529 разобьем на группы по две цифры, начиная справа;
'    2) для старшей группы, образующей число 27, подберем такую цифру, чтобы ее квадрат был наибольшим, но не превосходил числа 27; такой цифрой будет 5, ее запишем в качестве первой цифры ответа;
'    3) из старшей группы цифр вычтем найденный в предыдущем пункте квадрат первой цифры ответа и к полученной разности 27 – 25 = 2 припишем справа следующую группу цифр 35; получим число 235;
'    4) удвоив записанное в ответе число 5, припишем справа такую цифру , чтобы произведение полученного в результате числа на эту цифру было наибольшим, но не превосходило числа 235; такой цифрой будет 2 (ибо 102 x 2 = 204 < 235, но 103 x 3 = 309 > 235), ее и запишем в качестве второй цифры ответа;
'    5) из числа 235 вычтем найденное в предыдущем пункте произведение 204 и к остатку 31 снесем следующую группу цифр 29; получим число 3129;
'    6) удвоив записанное в ответе число 52, припишем справа такую цифру, чтобы произведение полученного в результате числа на эту цифру было наибольшим, но не превосходило числа 3129; такой цифрой будет 3 (ибо 1043 x 3 = 3129), ее и запишем в качестве третьей цифры ответа;
'    7) разность между снесенным числом 3129 и полученным в предыдущем пункте произведением равна 0, поэтому корень квадратный из числа 273529 извлекается нацело и равен записанному в ответе числу 523.
'}
 
 
'############################################################################################
'###                 [url]http://www.vbnet.ru/articles/showarticle.aspx?id=92[/url]                  ###
'############################################################################################
Public Function ResetZero(ByVal N1 As String) As String
 
    ' просто убираем лишние нули из начала строки (числа): 0004562 -> 4562
 
    On Error Resume Next
    Dim i As Long, Tmp As String
    For i = 1 To Len(N1)
        If Mid$(N1, i, 1) <> "0" Then Exit For    ' передвигаем указатель i по числу, пока на встретим не 0
    Next
    Tmp = Right$(N1, Len(N1) - i + 1)    ' Вырезаем ненужный мусор
 
    ' Если строка состояла из одних нулей, то оставим один нолик на память!
 
    If Tmp = "" Then ResetZero = "0" Else ResetZero = Tmp
End Function
 
Public Function isLarger(ByVal N1 As String, ByVal N2 As String) As Boolean
    On Error Resume Next
    Dim L As Long, i As Long, B1 As Byte, B2 As Byte
    L = Len(N1)
    If L <> Len(N2) Then
        isLarger = L > Len(N2)    'если длины чисел не равны, то больше то, чья длина больше!
    Else
 
        ' Теперь длины чисел равны…
 
        If N1 Like N2 Then
            isLarger = True    ' проверка случая N1=N2
            Exit Function
        End If
        isLarger = False    ' теперь: N1<>N2 и у них одинаковая длина -> поразрядная проверка:
        For i = 1 To L
            B1 = CByte(Mid$(N1, i, 1))
            B2 = CByte(Mid$(N2, i, 1))
            Select Case B1
            Case Is < B2: Exit Function    ' N1<N2
            Case Is > B2    ' N2<N1
                isLarger = True
                Exit Function
            End Select
        Next
    End If
End Function
 
Public Function LongAdd(ByVal N1 As String, ByVal N2 As String) As String
 
    ' Самая нужная и полезная функция (также как и вычитание)! Ведь на её основе можно сделать умножение и возведение в степень.
    ' На основе вычитания можно сделать деление и MOD
 
    On Error Resume Next
    Dim Tmp As String, Pre As String, Mas() As Byte, L As Long, i As Long
 
    ' сделаем так, чтобы в N1 хранилось более длинное число, к N2 припишем недостающие разряды нулями.
 
    If Len(N2) > Len(N1) Then
        Tmp = N1
        N1 = N2
        N2 = Tmp
    End If
    L = Len(N1)
    N2 = String$(L - Len(N2), "0") & N2
 
    ' А вот и массив! Заметьте, что его размер 1..L. Хотя в результате может получиться число длиной максимум L+1
 
    ReDim Mas(1 To L)
 
    ' Заполняем каждый элемент массива соответствующей суммой
 
    For i = 1 To L
        Mas(i) = CByte(Mid$(N1, i, 1)) + CByte(Mid$(N2, i, 1))
    Next
 
    ' Начиная с предпоследней ячейки, нормализуем массив. Число 48 прибавляется, чтобы правильно конвертировать массив в String.
 
    For i = L - 1 To 1 Step -1
        If Mas(i + 1) > 9 Then Mas(i) = Mas(i) + 1
        Mas(i + 1) = (Mas(i + 1) Mod 10) + 48
    Next
 
    ' Проверяем, не вылез ли L+1 разряд
 
    If Mas(1) > 9 Then
        Pre = "1"
        Mas(1) = Mas(1) + 38    ' 38=48-10
    Else
        Pre = ""
        Mas(1) = Mas(1) + 48
    End If
 
    ' Конвертируем массив в String
 
    Tmp = StrConv(Mas, vbUnicode)
    i = InStr(Tmp, Chr(0))
    If i > 0 Then Tmp = Left$(Tmp, i - 1)
 
    ' Опля, всё готово!
 
    LongAdd = Pre & Tmp
End Function
 
Public Function LongSub(ByVal N1 As String, ByVal N2 As String) As String
 
    ' действие обратное сложению, не менее полезно. Начало эквивалентно LONGADD:
 
    On Error Resume Next
    Dim Tmp As String, Mas() As Byte, L As Long, i As Long
    If Len(N2) > Len(N1) Then
        Tmp = N1
        N1 = N2
        N2 = Tmp
    End If
    L = Len(N1)
    N2 = String$(L - Len(N2), "0") & N2
    ReDim Mas(1 To L)
    For i = 1 To L
        Mas(i) = 10 + CByte(Mid$(N1, i, 1)) - CByte(Mid$(N2, i, 1))    ' а здесь отнимаем!
    Next
    For i = L - 1 To 1 Step -1
        If Mas(i + 1) < 10 Then Mas(i) = Mas(i) - 1 Else Mas(i) = Mas(i)    ' и здесь небольшое отличие
        Mas(i + 1) = Mas(i + 1) Mod 10
    Next
    Mas(1) = Mas(1) Mod 10    ' проверка на L+1 разряд не нужна.
    For i = 1 To L
        Mas(i) = Mas(i) + 48    ' корректируем значения для StrConv
    Next
 
    ' MAS->String
 
    Tmp = StrConv(Mas, vbUnicode)
    i = InStr(Tmp, Chr(0))
    If i > 0 Then Tmp = Left$(Tmp, i - 1)
 
    ' Убираем лишние нули из начала числа
 
    Tmp = ResetZero(Tmp)
 
    ' Если N1=N2, то мы столкнёмся с этой неприятной ситуацией:
 
    If Tmp = "" Then LongSub = "0" Else LongSub = Tmp
End Function
 
Public Function LongMult(ByVal N1 As String, ByVal N2 As String) As String
    On Error Resume Next
    Dim Tmp As String, Mas() As Long, L As Long, i As Long, J As Long, ByteMas() As Byte, P As Long
 
    ' в начале как всегда!
 
    If Len(N2) > Len(N1) Then
        Tmp = N1
        N1 = N2
        N2 = Tmp
    End If
    L = Len(N1)
    N2 = String$(L - Len(N2), "0") & N2
 
    ' Массив берём из 2*L элементов. Т.к. 99*99=9801
 
    ReDim Mas(1 To 2 * L)
    For i = 1 To L * 2
        Mas(i) = 0
    Next
    For i = L To 1 Step -1
        For J = L To 1 Step -1    ' двойной цикл… вот тут-то и влияние на скорость!
            P = i + J    ' адрес куда будем класть результат. Вспоминайте перемножение столбиком!
            Mas(P) = Mas(P) + CLng(Mid$(N1, J, 1)) * CLng(Mid$(N2, i, 1))
        Next
    Next
 
    'знакомая нам нормализация массива начинаемая с предпоследнего элемента
 
    P = 2 * L - 1
    For i = P To 1 Step -1
        J = i + 1
        Mas(i) = Mas(i) + Int(Mas(J) / 10)
        Mas(J) = (Mas(J) Mod 10) + 48
    Next
    Mas(1) = Mas(1) + 48
 
    'теперь Long mas -> byte mas (для того, чтобы можно было использовать StrConv)
 
    ReDim ByteMas(1 To 2 * L)
    P = 2 * L
    For i = 1 To P
        ByteMas(i) = CByte(Mas(i))
    Next
 
    ' тоже как всегда...
 
    Tmp = StrConv(ByteMas, vbUnicode)
    i = InStr(Tmp, Chr(0))
    If i > 0 Then Tmp = Left$(Tmp, i - 1)
    LongMult = ResetZero(Tmp)    'и последний штрих: удаляем лишние нули. А их может быть очень много
End Function
 
 
'Sub sqr2()
'    ' 665857/470832 = 1.4142135623746…
'    Dim sOst As String
'    Debug.Print LongDiv("665857000000000000000000000", 470832, sOst)
'End Sub
 
'Public Function LongDiv(ByVal N1 As String, ByVal N2 As String, ByRef Ostatok As String) As String
'    On Error Resume Next
'    Dim i As Long, Tmp As String, Mas(1 To 9) As String, IntPart As String, PreIntPart As String
'    LongDiv = "0"
'    Ostatok = "0"
'
'    ' В начале некоторые частные случаи деления:
'
'    If N1 = "0" Then Exit Function
'    If N2 = "0" Then
'        MsgBox "ERROR: Division by Zero!"
'        Exit Function
'    End If
'    If N2 = "1" Then
'        LongDiv = N1
'        Ostatok = "0"
'        Exit Function
'    End If
'    If N1 Like N2 Then
'        LongDiv = "1"
'        Exit Function
'    End If
'    If isLarger(N2, N1) Then
'        Ostatok = N1
'        Exit Function
'    End If
'
'    ' А дальше несколько дополненный LongMod. Пусть разбор этого примера будет на Вашей совести
'
'    IntPart = "0"
'    Ostatok = N1
'    Do While isLarger(Ostatok, N2)
'        Tmp = N2 & String$(Len(Ostatok) - Len(N2), "0")
'        PreIntPart = "1" & String$(Len(Ostatok) - Len(N2), "0")
'        If Tmp Like Ostatok Then
'            LongDiv = LongAdd(IntPart, PreIntPart)
'            Ostatok = "0"
'            Exit Function
'        End If
'        If isLarger(Tmp, Ostatok) Then
'            Tmp = Left$(Tmp, Len(Tmp) - 1)
'            PreIntPart = Left$(PreIntPart, Len(PreIntPart) - 1)
'        End If
'        Mas(1) = Tmp
'        For i = 2 To 10
'            Mas(i) = LongAdd(Mas(i - 1), Tmp)
'            If isLarger(Mas(i), Ostatok) Or (Mas(i) Like Ostatok) Then Exit For
'        Next
'        IntPart = LongAdd(IntPart, Replace$(PreIntPart, "1", i - 1))
'        Ostatok = LongSub(Ostatok, Mas(i - 1))
'    Loop
'    LongDiv = IntPart
'End Function
используем так:
Visual Basic
1
MsgBox LongSqr(2,100)
6
381 / 4 / 3
Регистрация: 20.03.2013
Сообщений: 43
05.10.2013, 00:31  [ТС] 7
аналитика, спасибо огромное!

Я правильно поняла, что ответ 1,4142135623731E+56?

Visual Basic
1
MsgBox Abs(Sqr(2) - LongSqr(2, 56))
Почему такая разница большая получилась?
0
Эксперт MS Access
26806 / 14485 / 3192
Регистрация: 28.04.2012
Сообщений: 15,782
05.10.2013, 03:14 8
Лучший ответ Сообщение было отмечено как решение

Решение

Але-Дашкова, предложенная выше функция дает строку без разделения на целую и дробную части.
Чтоб не копаться в кишках кода, можно поправить уже полученный результат. В функции LongSqr, вместо строки LongSqr = sRes (после строки Debug.Print Timer - sngT) напишите код
Visual Basic
1
2
3
4
5
6
7
    Dim d As Double
    d = Sqr(iNum)
    If Left(Int(d), 1) = 0 Then
        LongSqr = "0." & sRes
    Else
        LongSqr = Left(sRes, InStr(1, d, ".") - 1) & "." & Mid(sRes, InStr(1, d, "."))
    End If
?LongSqr(2, 56)
1.41421356237309504880168872420969807856967187537694807317
3
381 / 4 / 3
Регистрация: 20.03.2013
Сообщений: 43
05.10.2013, 11:52  [ТС] 9
Лучший ответ Сообщение было отмечено как решение

Решение

mobile, спасибо! Долго разбиралась, наконец решила через формулы Excel. Перевести формулы Excel в VBA смогу сама, не переживайте!
Вложения
Тип файла: xlsx КалькуляторСуммыРазности.xlsx (16.5 Кб, 6 просмотров)
2
05.10.2013, 11:52
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
05.10.2013, 11:52
Помогаю со студенческими работами здесь

Посмотреть код встроенных математических функций. Открыть cmath
Каким образом можно посмотреть, как в c++ считаются встроенные функции в библиотеке cmath(math.h) ?...

Вычисление значений функций с заданной точностью
Цель работы: овладеть навыками использования операторов WHILE и REPEAT . Порядок выполнения работы...

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

Вычислить выражение c заданной точностью, используя формулы разложения функций в ряд Тейлора
3*pi/12+e^0.98+sqrt(1.28)+sin(pi/12)


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
9
Ответ Создать тему
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru