为啥使用整数而不是长整数?
Posted
技术标签:
【中文标题】为啥使用整数而不是长整数?【英文标题】:Why Use Integer Instead of Long?为什么使用整数而不是长整数? 【发布时间】:2014-12-12 02:21:45 【问题描述】:我经常看到与Overflow
错误相关的问题vba。
我的问题是为什么要使用integer
变量声明,而不是将所有数值变量(不包括double
等)定义为long
?
除非您在 for 循环中执行操作,您可以保证该值不会超过 32,767 的限制,否则是否会对性能产生影响或其他什么会导致不使用 long
?
【问题讨论】:
【参考方案1】:VBA 有很多历史包袱。
Integer
是 16 位宽,在 16 位架构/字长盛行时是一种很好的默认数字类型。
Long
是 32 位宽,应尽可能使用 (IMO)。
【讨论】:
【参考方案2】:整数变量存储为 16 位(2 字节)数字
Office VBA Reference
长(长整数)变量存储为带符号的 32 位(4 字节)数字
Office VBA Reference
因此,好处是减少了内存空间。 Integer 占用 Long 的一半内存。现在,我们谈论的是 2 个字节,因此它不会对单个整数产生真正的影响,只有在处理大量整数(例如大型数组)并且内存使用至关重要时才会出现问题。
但是在 32 位系统上,内存使用量减半是以性能为代价的。当处理器实际使用 16 位整数执行一些计算(例如,增加循环计数器)时,该值会默默地转换为临时 Long,而没有使用更大范围的数字的好处。仍然会发生溢出,并且处理器用于存储计算值的寄存器将占用相同数量的内存(32 位)。 Performance may even be hurt 因为必须转换数据类型(在非常低的级别)。
不是我要找的参考资料,而是....
我的理解是,底层的 VB 引擎将整数转换为 long,即使它被声明为整数。因此可以注意到轻微的速度降低。我相信这一点已有一段时间了,也许这也是为什么做出上述陈述的原因,我没有要求推理。
ozgrid forums
这是我要找的参考资料。
简答,在 32 位系统中,2 字节整数转换为 4 字节 多头。确实没有其他方法可以使各个位正确排列 适合任何形式的处理。考虑以下
MsgBox Hex(-1) = Hex(65535) ' = True
显然 -1 不等于 65535 但计算机返回的是正确的 答案,即 "FFFF" = "FFFF"
但是,如果我们将 -1 强制为 long first,我们会得到正确的 答案(大于 32k 的 65535 自动长)
MsgBox Hex(-1&) = Hex(65535) ' = False
"FFFFFFFF" = "FFFF"
在现代 VBA 中通常没有必要声明“As Integer” 系统,除了一些期望接收 整数。
pcreview forum
我终于找到了我真正想要的msdn documentation。
传统上,VBA 程序员使用整数来保存较小的 数字,因为它们需要更少的内存。在最近的版本中, 但是,VBA 将所有整数值转换为 Long 类型,即使它们是 声明为整数类型。所以不再有性能优势 使用整数变量;其实 Long 变量可能会略 更快,因为 VBA 不必转换它们。
根据 cmets 进行澄清:整数仍然需要更少的内存来存储 - 与具有相同维度的 Long 数组相比,大型整数数组需要的 RAM 少得多。但由于处理器需要处理 32 位内存块,VBA 在执行计算时临时将整数转换为长整数
因此,总而言之,现在几乎没有充分的理由使用Integer
类型。 除非您需要与需要 16 位 int 的旧 API 调用进行互操作,或者您正在处理包含小整数的大型数组并且内存非常宝贵。 p>
值得指出的一点是,一些旧的 API 函数可能需要 16 位(2 字节)整数的参数,如果您使用 32 位并尝试传递整数(即已经是 4- byte long) 通过引用,由于字节长度的不同,它将无法工作。
感谢 Vba4All 指出这一点。
【讨论】:
值得指出的一点是,一些旧的 API 函数可能需要 16 位(2 字节)整数的参数,如果您使用 32 位并尝试传递整数(即已经是 4 字节长)通过引用,由于字节长度的不同,它将无法工作。 @It'sbeenapleasure 我不明白。如果将 Long by ref 传递给期望 Integer by ref 的函数,它将使用前两个字节,并且由于数字存储在 little-endian 中,因此它将起作用(前提是只有两个较低的字节是有意义的,但是如果这个 Long 只是一个以 32 位存储的整数,则很可能)。 您必须使用Integer
的其他时间是在声明Type
时,其中类型的布局和大小很重要,因为您正在传递类型到 API,或者您正在序列化/反序列化文件,或者您正在使用 LSet
/Rset
复制字节。
那篇 MSDN 文章是错误的和/或误导性的。它可能指的是例如Integer
s 在处理器内部被作为Long
s 处理,但出于所有可观察的目的,Integer
仍然需要两个字节。请参阅***.com/q/26717148/11683 下的答案和 cmets 以进行扩展讨论。
您似乎没有在您的回答中说明msdn.microsoft.com/en-us/library/office/… 可能是错误或具有误导性的,而是您将其呈现为the msdn documentation I was really truly looking for.
但是我的评论主要针对未来的读者而不是你自己,因为你在另一个问题中have participated,即使在我看来你的答案不应该被接受(this one 我相信应该被接受)。【参考方案3】:
正如其他答案所指出的,int 和 long 之间的真正区别在于其内存空间的大小,因此它可以容纳的数字的大小。
这里是这些数据类型的完整文档 http://msdn.microsoft.com/en-us/library/office/ms474284(v=office.14).aspx
整数是 16 位,可以表示介于 -32,768 和 32,767 之间的值
a Long 为 32 位,可以表示 -2,147,483,648 到 2,147,483,647
还有一个 LongLong,它是 64 位的,可以像 9 pentilion 一样处理
在这方面要记住的最重要的事情之一是数据类型因语言和操作系统/平台而异。在您的 VBA 世界中,long 是 32 位,但在 64 位处理器上的 c# 中,long 是 64 位。这可能会引起严重的混乱。
虽然 VBA 不支持它,但当你在 .net 或 java 或其他语言中迁移到任何其他语言时,我更喜欢使用 int16、int32 和 int64 让我可以更加透明地了解可以在这些数据类型中保存的值。
【讨论】:
【参考方案4】:这是一个空间与必要性的问题。
在某些情况下,必须使用长。如果您在大型 Excel 文件中循环遍历行,则保存行号的变量应该很长。
但是,有时您会知道整数可以解决您的问题,而使用 long 会浪费 空间(内存)。单个变量确实没有太大的区别,但是当您开始处理数组时,它会产生很大的不同。
在 VBA7 中,整数是 2 个字节,长整数是 4 个字节
如果您有一个包含 100 万个介于 1 和 10 之间的数字的数组,则使用整数数组将占用 大约 2MB 的 RAM,而长数组大约需要 4MB 的 RAM。
【讨论】:
对于 1 到 10 之间的数字数组,您可以改用 BYTE 数组,但我理解您的意思。 这在大约 15 年前可能是正确的,但现在答案真的已经过时了。 这怎么过时了? pandas 中存在数百万行的问题(大数据/机器学习),或传输大量数据时的网络问题(例如物理实验) @Alter - 我猜你错过了原始问题的重点。看看接受的答案。 看来我明白了。接受的答案还指出,当它说没有区别时,它是在谈论 32 位系统。【参考方案5】:尽管这篇文章已经发布了四年,但我对此感到好奇并进行了一些测试。需要注意的最重要的一点是,编码人员应该始终将变量声明为 SOMETHING。未声明的变量显然表现最差(未声明在技术上是Variant
)
Long
确实执行得最快,所以我不得不认为微软建议始终使用Long
而不是Integer
是有道理的。我猜Byte
的情况与此相同,但大多数编码人员不使用它。
在 64 位 WINDOWS 10 笔记本电脑上的结果
使用的代码:
Sub VariableOlymics()
'Run this macro as many times as you'd like, with an activesheet ready for data
'in cells B2 to D6
Dim beginTIME As Double, trials As Long, i As Long, p As Long
trials = 1000000000
p = 0
beginTIME = Now
For i = 1 To trials
Call boomBYTE
Next i
Call Finished(p, Now - beginTIME, CDbl(trials))
p = p + 1
beginTIME = Now
For i = 1 To trials
Call boomINTEGER
Next i
Call Finished(p, Now - beginTIME, CDbl(trials))
p = p + 1
beginTIME = Now
For i = 1 To trials
Call boomLONG
Next i
Call Finished(p, Now - beginTIME, CDbl(trials))
p = p + 1
beginTIME = Now
For i = 1 To trials
Call boomDOUBLE
Next i
Call Finished(p, Now - beginTIME, CDbl(trials))
p = p + 1
beginTIME = Now
For i = 1 To trials
Call boomUNDECLARED
Next i
Call Finished(p, Now - beginTIME, CDbl(trials))
p = p + 1
End Sub
Private Sub boomBYTE()
Dim a As Byte, b As Byte, c As Byte
a = 1
b = 1 + a
c = 1 + b
c = c + 1
End Sub
Private Sub boomINTEGER()
Dim a As Integer, b As Integer, c As Integer
a = 1
b = 1 + a
c = 1 + b
c = c + 1
End Sub
Private Sub boomLONG()
Dim a As Long, b As Long, c As Long
a = 1
b = 1 + a
c = 1 + b
c = c + 1
End Sub
Private Sub boomDOUBLE()
Dim a As Double, b As Double, c As Double
a = 1
b = 1 + a
c = 1 + b
c = c + 1
End Sub
Private Sub boomUNDECLARED()
a = 1
b = 1 + a
c = 1 + b
c = c + 1
End Sub
Private Sub Finished(i As Long, timeUSED As Double, trials As Double)
With Range("B2").Offset(i, 0)
.Value = .Value + trials
.Offset(0, 1).Value = .Offset(0, 1).Value + timeUSED
.Offset(0, 2).FormulaR1C1 = "=ROUND(RC[-1]*3600*24,0)"
End With
End Sub
【讨论】:
未声明的变量是一个变体。Dim a as Variant, b as Variant, c As Variant
应该给出与根本不“变暗”相同的结果。
@Vityata,我知道“未声明的”应该意味着变体,但请记住,整个讨论的背景是微软自动将整数转换为 Long,尽管已定义!所以我不确定没有定义的整数会发生什么。看到结果,它似乎最有可能是真正的变体,但这就是为什么我将名称命名为“未声明”与“变体”。另外,这可能对较低级别的编码人员更有用。但是,我会继续添加现在就可以了。感谢您的反馈。
@PGCodeRider - 用 c++ 创建一个 dll 库并将其添加到 VBA 并在那里对其进行基准测试可能会很有趣。结果可能会很有趣。我在这里做了类似的事情 - vitoshacademy.com/…【参考方案6】:
我采用了@PGSystemTester 的方法并对其进行了更新以消除一些潜在的可变性。通过将循环放置在例程中,这消除了调用例程所花费的时间(这是很多时间)。我还关闭了屏幕更新以消除这可能导致的任何延迟。
Long
仍然表现最好,并且由于这些结果更接近于仅受变量类型的影响,因此值得注意的变化幅度。
我的结果(桌面、Windows 7、Excel 2010):
使用的代码:
Option Explicit
Sub VariableOlympics()
'Run this macro as many times as you'd like, with an activesheet ready for data
'in cells B2 to D6
Dim beginTIME As Double, trials As Long, i As Long, p As Long
Dim chosenWorksheet As Worksheet
Set chosenWorksheet = ThisWorkbook.Sheets("TimeTrialInfo")
Application.EnableEvents = False
Application.Calculation = xlCalculationManual
Application.ScreenUpdating = False
trials = 1000000000 ' 1,000,000,000 - not 10,000,000,000 as used by @PGSystemTester
p = 0
beginTIME = Now
boomBYTE trials
Finished p, Now - beginTIME, CDbl(trials), chosenWorksheet.Range("B2")
p = p + 1
beginTIME = Now
boomINTEGER trials
Finished p, Now - beginTIME, CDbl(trials), chosenWorksheet.Range("B2")
p = p + 1
beginTIME = Now
boomLONG trials
Finished p, Now - beginTIME, CDbl(trials), chosenWorksheet.Range("B2")
p = p + 1
beginTIME = Now
boomDOUBLE trials
Finished p, Now - beginTIME, CDbl(trials), chosenWorksheet.Range("B2")
p = p + 1
beginTIME = Now
boomUNDECLARED trials
Finished p, Now - beginTIME, CDbl(trials), chosenWorksheet.Range("B2")
p = p + 1
Application.EnableEvents = True
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
chosenWorksheet.Calculate
End Sub
Private Sub boomBYTE(numTrials As Long)
Dim a As Byte, b As Byte, c As Byte
Dim i As Long
For i = 1 To numTrials
a = 1
b = 1 + a
c = 1 + b
c = c + 1
Next i
End Sub
Private Sub boomINTEGER(numTrials As Long)
Dim a As Integer, b As Integer, c As Integer
Dim i As Long
For i = 1 To numTrials
a = 1
b = 1 + a
c = 1 + b
c = c + 1
Next i
End Sub
Private Sub boomLONG(numTrials As Long)
Dim a As Long, b As Long, c As Long
Dim i As Long
For i = 1 To numTrials
a = 1
b = 1 + a
c = 1 + b
c = c + 1
Next i
End Sub
Private Sub boomDOUBLE(numTrials As Long)
Dim a As Double, b As Double, c As Double
Dim i As Long
For i = 1 To numTrials
a = 1
b = 1 + a
c = 1 + b
c = c + 1
Next i
End Sub
Private Sub boomUNDECLARED(numTrials As Long)
Dim a As Variant, b As Variant, c As Variant
Dim i As Long
For i = 1 To numTrials
a = 1
b = 1 + a
c = 1 + b
c = c + 1
Next i
End Sub
Private Sub Finished(i As Long, timeUSED As Double, trials As Double, initialCell As Range)
With initialCell.Offset(i, 0)
.Value = trials
.Offset(0, 1).Value = timeUSED
.Offset(0, 2).FormulaR1C1 = "=ROUND(RC[-1]*3600*24,2)"
End With
End Sub
【讨论】:
我实际上比我更喜欢这种方法。【参考方案7】:正如其他人已经提到的,Long 可能占用的空间是 Integer 的两倍。正如其他人已经提到的那样,当前计算机的高容量意味着您将看到性能没有任何差异,除非您正在处理额外的额外大量数据:
内存
考虑到 100 万个值,使用整数与长整数之间的差异将是每个值 2 个字节,因此 2 * 1 000 000 / 1,024 / 1024 = 小于 2您的 RAM 中的 MB 差异,这可能远小于 RAM 容量的 1% 甚至 0.1%。
处理
考虑到 PGSystemTester 所做的基准测试,当处理 100 亿批(每批 4 次操作)时,您可以看到 Long 和 Integer 之间的差异为 811 - 745 = 66 秒。将数量减少到 100 万次操作,我们可以预期 66 / 10 000 / 4 = 执行时间差异小于 2 毫秒。
我个人使用整数和长整数来提高我的代码的可读性,特别是在循环中,整数表示循环应该很小(小于 1000 次迭代),而长整数告诉我预计循环会相当大(超过 1000 个)。
请注意,这个主观阈值远低于整数上限,我使用 Longs 只是为了区分我自己对小和大的定义。
【讨论】:
以上是关于为啥使用整数而不是长整数?的主要内容,如果未能解决你的问题,请参考以下文章