Почему эта пользовательская функция в Excel VBA не работает должным образом. Что-то не так с кодом или алгоритмом? - PullRequest
0 голосов
/ 27 января 2019

Я написал код для пользовательской функции для добавления чисел (больше 15 цифр) в Excel VBA. Это работает нормально, если я запускаю эту функцию 1 раз, но выдает ошибку при использовании несколько раз. Я новичок в этой области, так что любая помощь действительно будет оценена.

Я уже написал код. Пожалуйста, посмотрите на это.

Public Function AddBigNumbers(n1 As String, n2 As String) As String
i1 = 0
i2 = 0
i3 = Int(Log(n1) / Log(10))
i4 = Int(Log(n2) / Log(10))
i5 = 0
i6 = 0

If i3 = i4 Then
    Dim Ad() As Long
    ReDim Ad(1 To i3 + 1) As Long
        For i2 = 1 To i3 + 1 Step 1
            Ad(i2) = (Mid(n1, i2, 1))
        Next i2

i1 = 0
i2 = 0
     Dim Bd() As Long
     ReDim Bd(1 To i3 + 1) As Long
        For i2 = 1 To i3 + 1 Step 1
            Bd(i2) = (Mid(n2, i2, 1))
        Next i2

Dim D() As Long
ReDim D(1 To i3 + 1) As Long

i1 = 0
     For i1 = 1 To i3 + 1 Step 1
        If Bd(i1) > Ad(i1) Then
            D(i1) = 1
        End If
     Next i1
i1 = 0
    For i1 = 1 To i3 + 1 Step 1
        If Bd(i1) = Ad(i1) Then
            D(i1) = 0
        End If
    Next i1
i1 = 0
    For i1 = 1 To i3 + 1 Step 1
        If Bd(i1) < Ad(i1) Then
            D(i1) = -1
        End If
    Next i1

i1 = 0
    For i1 = 1 To i3 + 1 Step 1
        If D(i1) > 0 Then i5 = n2 And i1 = i3 + 1 Else If D(i1) = 0 Then i1 = i1 Else i5 = n1
    Next i1
i1 = 0
    For i1 = 1 To i3 + 1 Step 1
        If D(i1) > 0 Then i6 = n1 And i1 = i3 + 1 Else If D(i1) = 0 Then i1 = i1 Else i6 = n2
    Next i1
End If

If i3 > i4 Then i5 = n1 Else i5 = n2
If i3 > i4 Then i6 = n2 Else i6 = n1


i7 = Int(Log(i5) / Log(10))
i8 = Int(Log(i6) / Log(10))

i1 = 0
i2 = 0
Dim A() As Long
ReDim A(1 To i7 + 1) As Long
    For i2 = 1 To i7 + 1 Step 1
        A(i2) = (Mid(i5, i2, 1))
    Next i2
i1 = 0
i2 = 0
    Dim B() As Variant
    ReDim B(1 To i7 + 1) As Variant
        If i7 > i8 Then
            For i1 = 1 To i7 - i8 Step 1
                B(i1) = 0
            Next i1

            For i2 = i7 - i8 + 1 To i7 + 1 Step 1
                B(i2) = Mid(i6, i2 - i7 + i8, 1)
            Next i2
        End If

         If i7 = i8 Then
             For i2 = 1 To i7 + 1 Step 1
                 B(i2) = Mid(i6, i2, 1)
             Next i2
         End If
i1 = 0
i2 = 0
    Dim C() As Variant
    ReDim C(1 To i7 + 1) As Variant
        For i2 = 1 To i7 + 1 Step 1
            C(i2) = CInt(A(i2)) + B(i2)
        Next i2

i1 = 0
i2 = 0
        For i2 = i7 + 1 To 2 Step -1
            C(i2 - 1) = C(i2 - 1) + Int(C(i2) / 10)
            C(i2) = C(i2) - 10 * Int(C(i2) / 10)
        Next i2
i9 = 0
i9 = Join(C, "")
'i9 = WorksheetFunction.Concat("'", Join(C, ""))
AddBigNumbers = i9
i1 = 0
i2 = 0
End Function

Он работает нормально, если я запускаю его один раз (то есть, если я использую его один раз), но несколько раз выдает ошибку "#Value".

Ответы [ 5 ]

0 голосов
/ 27 января 2019

«Теорема сложения» согласно вашей картинке https://drive.google.com/file/d/1Hny1dCMWUIUwQ19lX82sJ0GaggxydRKC/view в псевдокоде:

    'step 1: add every digit of the input strings
    'step 2: for each result above 10:
    'step 3:    reduce by 10 and add 1 to the result of the neighbour

Ваш алгоритм разбивается на более длинные строки (свыше ~ 300 цифр):

enter image description here

Public Function AddBig(n1 As String, n2 As String) As String

Dim A(10000) As Integer
Dim B(10000) As Integer
Dim r(10000) As Integer
Dim iCt As Integer
Dim lenCt As Integer
Dim DebugStr As String
Dim Plus1 As Boolean

'Dim n1 As String, n2 As String

'n1 = Range("b3")
'n2 = Range("b6")

    For iCt = Len(n1) To 1 Step -1
        A(iCt) = Mid(n1, Len(n1) - iCt + 1, 1)
    Next iCt

    For iCt = Len(n2) To 1 Step -1
        B(iCt) = Mid(n2, Len(n2) - iCt + 1, 1)
    Next iCt

    lenCt = Application.Max(Len(n1), Len(n2))

    For iCt = lenCt To 1 Step -1
        r(iCt) = A(iCt) + B(iCt)
    Next iCt

    For iCt = 1 To lenCt
        If r(iCt) > 9 Then
            r(iCt) = r(iCt) - 10
            r(iCt + 1) = r(iCt + 1) + 1
            If iCt = lenCt Then Plus1 = True
        End If
        resultStr = r(iCt) & resultStr
    Next iCt
    If Plus1 Then resultStr = "1" & resultStr
    Debug.Print resultStr
    AddBig = resultStr
End Function
0 голосов
/ 27 января 2019

«Теорема сложения» согласно вашей картинке https://drive.google.com/file/d/1Hny1dCMWUIUwQ19lX82sJ0GaggxydRKC/view в псевдокоде:

    'step 1: add every digit of the input strings
    'step 2: for each result above 10:
    'step 3:    reduce by 10 and add 1 to the result of the neighbour
0 голосов
/ 27 января 2019

Пожалуйста, попробуйте код ниже.

Function SumBigNumbers(s1 As String, s2 As String) As String

    Dim n1 As Double, n2 As Double

    n1 = CDbl(s1)
    n2 = CDbl(s2)
    SumBigNumbers = CStr(n1 + n2)
End Function

Код просто преобразует две строки длинных чисел в реальные числа, суммирует их и преобразовывает результат в строку. VBA вернет строку числа в научной нотации. Обратите внимание, однако, что это только для целей отображения. Внутренне это нормальное (если длинное) число.

Преобразование во что-то читаемое происходит, следовательно, после вычисления результата, то есть в вызывающей процедуре.

Private Sub CallSum()

    With ActiveSheet.Cells(16, 1)
        .Value = SumBigNumbers("12345678987654321", "34567890123987654")
        .NumberFormat = "0.00"
    End With
End Sub

Как оказалось, простой NumberFormat сделает эту работу. Мне просто интересно, будет ли это работать с гораздо большими числами, как это происходит в моем примере.

0 голосов
/ 27 января 2019

Если вы диммируете i1 .. i9 как целое число, вы обнаружите, что ваш алгоритм выходит за пределы целочисленного предела.Всякий раз, когда вы назначаете входные строки n1 или n2 одной из переменных ix, вы рискуете нарушить ваш код (вы теряете возможность добавлять большие числа).

...
If D(i1) > 0 Then i5 = n2 And i1 = i3 + 1 Else If D(i1) = 0 Then i1 = i1 Else i5 = n1  'line 47
...
If D(i1) > 0 Then i6 = n1 And i1 = i3 + 1 Else If D(i1) = 0 Then i1 = i1 Else i6 = n2  'line 51
...
If i3 > i4 Then i5 = n1 Else i5 = n2                                                   'line 55
If i3 > i4 Then i6 = n2 Else i6 = n1                                                   'line 56
...

Если вы просто хотите решить проблему (до 200 цифр): используйте xnumbers (https://en.wikipedia.org/wiki/XNUMBERS)

). Если вы хотите научиться кодировать: используйте «Option Explicit» и используйте много строк debug.print.например, Debug.Print "Step x: "; i1; " "; i2; " "; i3; " "; i4; " "; i5; " "; i6; " "; i7; " "; i8; " "; i9

Почему бы вам просто не добавить входные строки символ за символом?(Ad (ix) + Bd (iX) и обработать переполнение)

0 голосов
/ 27 января 2019

недостаток в двух строках:

If D(i1) > 0 Then i5 = n2 And i1 = i3 + 1 Else If D(i1) = 0 Then i1 = i1 Else i5 = n1

If D(i1) > 0 Then i6 = n1 And i1 = i3 + 1 Else If D(i1) = 0 Then i1 = i1 Else i6 = n2

с учетом первой и перевод в синтаксис plain If, это будетyield:

    If D(i1) > 0 Then
        i5 = n2 And i1 = i3 + 1
    Else
        If D(i1) = 0 Then
            i1 = i1
        Else
            i5 = n1
        End If
    End If

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

    If D(i1) > 0 Then
        i5 = n2
        i1 = i3 + 1 ' to end loop
    Else
        If D(i1) = 0 Then
            i1 = i1
        Else
            i5 = n1
        End If
    End If

Кроме того:

  • для выхода из цикла, который вы хотите использовать Exit For оператор
  • эти две For i1 = 1 To i3 + 1 Step 1 петли могут быть объединены вместе
  • i1 = i1 ничего не делает

, и поэтому вы можете переписать эти две петли в одну петлю:

    For i1 = 1 To i3 + 1 Step 1
        If D(i1) > 0 Then
            i5 = n2
            i6 = n1
            Exit For
        ElseIf D(i1) < 0 Then
            i5 = n1
            i6 = n2
        End If
    Next i1

Наконец, если добавить явное объявление переменных и некоторые другие оптимизации, весь код приведет к следующему:

Public Function AddBigNumbers(n1 As String, n2 As String) As String
    Dim i1 As Long, i2 As Long, i3 As Long, i4 As Long
    Dim i5 As Variant, i6 As Variant
    Dim i7 As Long, i8 As Long

    i3 = Int(Log(n1) / Log(10))
    i4 = Int(Log(n2) / Log(10))
    i5 = 0
    i6 = 0

    Select Case True
        Case i3 = i4
            ReDim Ad(1 To i3 + 1) As Long
            ReDim Bd(1 To i3 + 1) As Long
            For i2 = 1 To i3 + 1 Step 1
                Ad(i2) = (Mid(n1, i2, 1))
                Bd(i2) = (Mid(n2, i2, 1))
            Next

            For i1 = 1 To i3 + 1
                If Bd(i1) > Ad(i1) Then
                    i5 = n2
                    i6 = n1
                    Exit For
                ElseIf Bd(i1) <= Ad(i1) Then
                    i5 = n1
                    i6 = n2
                End If
            Next

        Case i3 > i4
            i5 = n1
            i6 = n2

        Case Else
            i5 = n2
            i6 = n1
    End Select


    i7 = Int(Log(i5) / Log(10))
    i8 = Int(Log(i6) / Log(10))

    Dim A() As Long
    ReDim A(1 To i7 + 1) As Long
    For i2 = 1 To i7 + 1
        A(i2) = (Mid(i5, i2, 1))
    Next

    Dim B() As Variant
    ReDim B(1 To i7 + 1) As Variant
    Select Case True
        Case i7 > i8
            For i1 = 1 To i7 - i8
                B(i1) = 0
            Next

            For i2 = i7 - i8 + 1 To i7 + 1
                B(i2) = Mid(i6, i2 - i7 + i8, 1)
            Next
        Case i7 = i8
            For i2 = 1 To i7 + 1
                B(i2) = Mid(i6, i2, 1)
            Next
    End Select

    Dim C() As Variant
    ReDim C(1 To i7 + 1) As Variant
    For i2 = 1 To i7 + 1
        C(i2) = CInt(A(i2)) + B(i2)
    Next

    For i2 = i7 + 1 To 2 Step -1
        C(i2 - 1) = C(i2 - 1) + Int(C(i2) / 10)
        C(i2) = C(i2) - 10 * Int(C(i2) / 10)
    Next

    AddBigNumbers = Join(C, "")

End Function
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...