我有一個問題,真的讓我難過。最終,我認爲這個問題很可能是因爲我缺乏關於Windows C編程的知識或者如何在C和VBA之間正常工作。Excel VBA不能很好地與小數的C DLL - 與整數工作正常
我一直在涉足VBA和C一段時間,我想我會結合他們的Excel項目,因爲VBA執行速度不是很快。
有時,在Excel中工作時,我需要在一長串值中找到一個值。對於一個值很容易(Ctrl + F),但有時我想要的值只能通過組合列表中的兩個或更多值來找到。
因爲這個原因,我寫了一個宏,它將值讀入一個數組,然後循環遍歷數組,嘗試將每個值與另一個值組合,以查看它們是否組合成爲我所尋求的值。現在我已經將循環部分移動到用C語言編寫的dll中,並且確實已經加快了速度,但是存在一個問題:大多數時間 - 但並非總是如此 - 它無法找到實際結合到值中的值尋找我所尋找的價值是否爲十進制值。
爲了嘗試找到問題出在哪裏,我讓dll將所有測試過的組合及其結果打印到一個文本文件中,我可以看到有匹配,但由於某種原因,我的if語句不會觸發在上面。
可能是什麼問題?
這裏是我的VBA代碼:
Private Declare Function FindVal Lib "mdvlib.dll" (ByRef dIn As Double, ByRef dOut As Double, ByVal iSizeIn As Long, ByVal sVal As Double, ByVal lvl As Long) As Long
Sub Match_Amounts(needle As Double, startcell As Range, level As Integer)
Dim haystack() As Variant
Dim i, j As Integer
Dim num As String
' dim variables going to the dll
Dim valArr() As Double
Dim valArr2() As Double
Dim arrSz, retval As Long
' read values from sheet into the array and find out its size
haystack() = Range(startcell, startcell.End(xlDown))
arrSz = UBound(haystack, 1)
're-dimension arrays that will be passed to the dll
ReDim valArr(1 To arrSz)
‘ using 100 here just to be on the safe side, will optimize later…
ReDim valArr2(1 To arrSz * 100)
' assign values
For i = 1 To arrSz
valArr(i) = haystack(i, 1)
Next
' change directory so that the macro finds the dll
ChDir Application.UserLibraryPath
' use the FindVal function in the dll
retval = FindVal(valArr(1), valArr2(1), arrSz, needle, level)
' present results
If retval > 0 Then
j = PresRes(valArr2, level, retval)
Else
num = Format(needle, "#,##0.00")
'Then show a message to the user
MsgBox "The value " & num & " could not be obtained by combining " & level & " values in the given range." _
& vbCr & vbLf & vbCr & vbLf _
& "This sometimes happens when searching for numbers with decimals. If this was the case, there could be values " _
& "that combine to make up the sought number.", vbInformation, "Match Amounts"
End If
Erase haystack
Erase valArr
Erase valArr2
End Sub
的PresRes子僅僅是將結果呈現給用戶的方式,不應該是相關的。不過,請讓我知道,如果你想看到它。
在DLL用於與VBA交互的功能我的C代碼是:
int __stdcall FindVal(double* dIn, double* dOut, int iSizeIn, double sVal, int lvl)
{
if(lvl == 2) return FindValTwo(dIn, dOut, iSizeIn, sVal);
if(lvl == 3) return FindValThree(dIn, dOut, iSizeIn, sVal);
if(lvl == 4) return FindValFour(dIn, dOut, iSizeIn, sVal);
return -1;
}
正如上面可以看出,我已經寫的C函數爲三種不同的方案,用於找到兩個,三個或四個加數但是在這裏我將只顯示用於查找兩個值的代碼,因爲代碼比其他代碼更緊湊和更簡單,並且我在所有這些函數中都遇到了問題。
下面是FindValTwo功能的代碼:
int FindValTwo(double* dIn, double* dOut, int iSizeIn, double sVal)
{
int i, j, k = 0;
FILE *dumpfile = NULL;
dumpfile = fopen("arraydump.txt", "a");
for(i = 0; i < iSizeIn; i++){
for(j = 0; j < iSizeIn; j++){
fprintf(dumpfile, "%f + %f = %f (%f) [%d][%d]\n", dIn[i], dIn[j], dIn[i] + dIn[j], sVal, i, j);
if(dIn[i] + dIn[j] == sVal && i != j){
fprintf(dumpfile, "\t^ found and added!\n");
if(ExistAlreadyTwo(dIn[i], dIn[j], dOut, k/2) == 0){
dOut[k + 0] = dIn[i];
dOut[k + 1] = dIn[j];
k += 2;
}
}
}
}
fclose(dumpfile);
return k;
}
上面參照文件寫入的線是有用於調試的目的,而不以其它方式包含。對於ExistAlreadyTwo函數的代碼是:
int ExistAlreadyTwo(double needle1, double needle2, double* haystack, int l)
{
// checks if the found values already exist in the return array
int i, existalready = 0;
for(i = 0; i < l; i++){
if((needle1 == haystack[i * 2] && needle2 == haystack[i * 2 + 1]) || (needle1 == haystack[i * 2 + 1] && needle2 == haystack[i * 2])){
existalready = 1;
break;
}
}
return existalready;
}
爲了測試,我做了Excel中的一個簡單的數組:
2.1
4.2
6.3
8.4
10.5
12.6
如果我搜索21我得到一擊的報告,它是8.4和12.6是結合成21.文本文件也驗證這一點:
8.400000 + 12.600000 = 21.000000 (21.000000) [3][5]
^found and added!
和位進一步下跌:
12.600000 + 8.400000 = 21.000000 (21.000000) [5][3]
^found and added!
然而,當搜索十進制值時,例如, 18。9,即使文件顯示這些值確實存在,並且與搜索到的值相結合,我也沒有得到任何結果。從文本文件輸出:
8.400000 + 10.500000 = 18.900000 (18.900000) [3][4]
而且
10.500000 + 8.400000 = 18.900000 (18.900000) [4][3]
由於兩位十進制值回升,如果他們合併成一個整數,起初我並不認爲這個問題是在陣列轉移到DLL,而是在轉移我正在尋找的價值。
不過,我想硬編碼在C中搜索值,在FindValTwo功能,用線:
sVal = 18.9;
...但是這並沒有幫助,也沒有被發現。文本文件看起來完全如上。
我已經嘗試了ByVal和ByRef(但只有ByRef的數組),但我得到了相同的結果。我使用32位Excel 2010(版本14.0.7163.5000)。
我發表了一些評論作爲答案。雖然他們可能沒什麼幫助 - 對不起 – HarveyFrench
可能是一個四捨五入的問題。一般來說,總是使用一個epsilon來比較浮點值,例如,如果(f1 + f2 == f3)'變成'if(abs(f1 + f2-f3)
謝謝。哈維,我已經評論了你的答案和保羅,謝謝你的評論。它確實具有很大的意義,我會研究它! – mdv