Lỗi Overflow khi chạy function

Liên hệ QC

kyo

Nguyễn Khắc Duy
Thành viên danh dự
Tham gia
4/6/06
Bài viết
901
Được thích
2,715
Chào mọi người,

kyo có 1 tình huống bị lỗi mà không hiểu vấn đề tại sao muốn nhờ mọi người giúp đỡ. Với đoạn code dưới đây thì kyo gặp lỗi Overflow

Mã:
Function tiendien(sodien As Double) As Double

If sodien <= 50 Then
    tiendien = sodien * 1000
Else
    tiendien = 50 * 1000 + (sodien - 50) * 2000
End If

End Function

Nhưng khi sửa 50 * 1000 thành 50000 thì code vẫn chạy như bình thường.

Mọi người giải thích giúp kyo với. Cảm ơn mọi người nhiều.
kyo.
 
Thấy cái pót kia mình mới nhớ ra đã từng chửi bới trình dịch VBE rồi. Bi giờ chửi thêm chút nữa.
Số Double thì tự gán #, nhưng số Long không chịu gán & cho người ta dễ debug 1 chút.
 
Upvote 0
Chào mọi người,

kyo có 1 tình huống bị lỗi mà không hiểu vấn đề tại sao muốn nhờ mọi người giúp đỡ. Với đoạn code dưới đây thì kyo gặp lỗi Overflow

Mã:
Function tiendien(sodien As Double) As Double

If sodien <= 50 Then
    tiendien = sodien * 1000
Else
    tiendien = 50 * 1000 + (sodien - 50) * 2000
End If

End Function

Nhưng khi sửa 50 * 1000 thành 50000 thì code vẫn chạy như bình thường.

Mọi người giải thích giúp kyo với. Cảm ơn mọi người nhiều.
kyo.
Hồi đó tôi phát hiện ra lỗi này nhờ... mò. Mở cửa sổ Immediate và thử:
- Gõ ?16000*2 rồi Enter ---> Không lỗi
- Gõ ?17000*2 rồi Enter ---> Lỗi
biết ngay kết quả bị lỗi nằm đâu đó trong khoảng 32000 và 34000. Lại cố gắng thử tiếp đến khi...
- Gõ ?16383*2 rồi Enter ---> Không lỗi
- Gõ ?16384*2 rồi Enter ---> Lỗi
Vậy con số bị lỗi là 16384*2 = 32768
thử tiếp
- Gõ ?-16384*2 rồi Enter ---> Không lỗi
Hai con số -32768 và 32767 này đã là gợi ý tốt nhất về lỗi mà không cần phải tìm hiểu ở bất cứ đâu cả
(không trường lớp, chỉ học mò nên hơi cực vậy đó. Dù sao cũng đến được với.. ngã ba)
 
Upvote 0
Cái function đơn giản này thì khai báo biến kiểu variant đi cho lành!!
 
Upvote 0
Cái function đơn giản này thì khai báo biến kiểu variant đi cho lành!!
Đâu phải vấn đề khai báo biến. Với Function ở bài 1, bạn cứ thử thay Double thành Variant xem có được không
Vấn đề nằm ở các phép tính trong VBA ấy (không phải biến). Mỗi khi mang các con số ra cộng trừ nhân chia trong VBA thì phải hết sức cẩn thận. Nó không đơn giản như khi gõ trên bảng tính. Lấy ví dụ đơn giản như bài 22 tôi vừa viết, đố bạn làm được phép tính 17000*2 trong VBA đó
 
Upvote 0
Đâu phải vấn đề khai báo biến. Với Function ở bài 1, bạn cứ thử thay Double thành Variant xem có được không
Vấn đề nằm ở các phép tính trong VBA ấy (không phải biến). Mỗi khi mang các con số ra cộng trừ nhân chia trong VBA thì phải hết sức cẩn thận. Nó không đơn giản như khi gõ trên bảng tính. Lấy ví dụ đơn giản như bài 22 tôi vừa viết, đố bạn làm được phép tính 17000*2 trong VBA đó
Vậy cho nên khai báo biến cho cả những hằng số như thế này:
PHP:
Function tiendien(sodien)
Dim Tinh
Dim DG1, DG2, Nac1
Nac1 = 50
DG1 = 1000
DG2 = 2000
If sodien <= Nac1 Then
    Tinh = sodien * DG1
Else
    Tinh = Nac1 * DG1 + (sodien - Nac1) * DG2
End If
tiendien = Tinh
End Function
 
Upvote 0
Vậy cho nên khai báo biến cho cả những hằng số như thế này:
PHP:
Function tiendien(sodien)
Dim Tinh
Dim DG1, DG2, Nac1
Nac1 = 50
DG1 = 1000
DG2 = 2000
If sodien <= Nac1 Then
    Tinh = sodien * DG1
Else
    Tinh = Nac1 * DG1 + (sodien - Nac1) * DG2
End If
tiendien = Tinh
End Function
Viết vậy thì còn nói làm gì (bạn sửa toàn bộ code của người ta rồi). Nếu code dùng 1000 con số, bạn đặt 1000 biến chắc?
Ở đây không bàn đến việc tính toán với các biến mà bàn về việc TÍNH TOÁN VỚI NHỮNG CON SỐ CỤ THỂ và tại sao đôi khi nó bị lỗi
Biết được vấn đề nằm ở đâu giúp ta chú ý hơn khi viết code
 
Upvote 0
Viết vậy thì còn nói làm gì (bạn sửa toàn bộ code của người ta rồi). Nếu code dùng 1000 con số, bạn đặt 1000 biến chắc?
Ở đây không bàn đến việc tính toán với các biến mà bàn về việc TÍNH TOÁN VỚI NHỮNG CON SỐ CỤ THỂ và tại sao đôi khi nó bị lỗi
Biết được vấn đề nằm ở đâu giúp ta chú ý hơn khi viết code
Vấn đề là GIẢI PHÁP thầy ạ! Em sẵn sàng bỏ toàn bộ code của em đi khi thấy 1 code khác ngon hơn! Đấy mới là học hỏi, mà học cách đấy mới khá lên được!
 
Upvote 0
Ở đây không bàn đến việc tính toán với các biến mà bàn về việc TÍNH TOÁN VỚI NHỮNG CON SỐ CỤ THỂ và tại sao đôi khi nó bị lỗi
Biết được vấn đề nằm ở đâu giúp ta chú ý hơn khi viết code
Thực ra khi thấy lỗi này thì em nghĩ mọi người cũng biết nguyên nhân tại sao và cách khắc phục, chỉ là có khi quên thôi. Chỉ cần chú ý nhất là biến interger dùng hậu tố là % dễ gây nhầm lẫn với phép tính phần trăm, ví dụ 50*100% lại ra kết quả 5000 chứ không phải 50 như ta đoán.
 
Upvote 0
Hồi đó tôi phát hiện ra lỗi này nhờ... mò. Mở cửa sổ Immediate và thử:
- Gõ ?16000*2 rồi Enter ---> Không lỗi
- Gõ ?17000*2 rồi Enter ---> Lỗi
biết ngay kết quả bị lỗi nằm đâu đó trong khoảng 32000 và 34000. Lại cố gắng thử tiếp đến khi...
- Gõ ?16383*2 rồi Enter ---> Không lỗi
- Gõ ?16384*2 rồi Enter ---> Lỗi
Vậy con số bị lỗi là 16384*2 = 32768
thử tiếp
- Gõ ?-16384*2 rồi Enter ---> Không lỗi
Hai con số -32768 và 32767 này đã là gợi ý tốt nhất về lỗi mà không cần phải tìm hiểu ở bất cứ đâu cả
(không trường lớp, chỉ học mò nên hơi cực vậy đó. Dù sao cũng đến được với.. ngã ba)
Cũng may là từ đó giờ em viết code, bao giờ cũng nhận giá trị từ một cái gì đó (biến, hằng, item, range v.v...) nên không bao giờ gặp lỗi! Nhờ bài này của Kyo thì nay em sẽ chú ý hơn khi ghi trực tiếp con số cụ thể để tính toán. Đúng là mọi thứ đều có giới hạn hoặc quy luật của nó bắt buộc ta phải tuân theo nếu ta tham gia cuộc chơi.
 
Upvote 0
Cũng may là từ đó giờ em viết code, bao giờ cũng nhận giá trị từ một cái gì đó (biến, hằng, item, range v.v...) nên không bao giờ gặp lỗi! Nhờ bài này của Kyo thì nay em sẽ chú ý hơn khi ghi trực tiếp con số cụ thể để tính toán. Đúng là mọi thứ đều có giới hạn hoặc quy luật của nó bắt buộc ta phải tuân theo nếu ta tham gia cuộc chơi.
Do vậy em đã từng nói với anh ở bài 85 https://www.giaiphapexcel.com/diend...iến-thức-căn-bản-vba.86400/page-5#post-539038
 
Upvote 0
Vậy cho nên khai báo biến cho cả những hằng số như thế này:
PHP:
Function tiendien(sodien)
Dim Tinh
Dim DG1, DG2, Nac1
Nac1 = 50
DG1 = 1000
DG2 = 2000
If sodien <= Nac1 Then
    Tinh = sodien * DG1
Else
    Tinh = Nac1 * DG1 + (sodien - Nac1) * DG2
End If
tiendien = Tinh
End Function
Dùng variant là đi "không đúng đường lối" của nhiều người ở đây: đường lối đặt trọng tâm vào tăng tốc.
Variant chậm hơn các kiểu số khác.
Đối với nhiều ngôn ngữ, dùng biến chậm hơn hằng. Tuy nhiên điều này tôi không chắc có áp dụng cho VBA hay không.

Cũng may là từ đó giờ em viết code, bao giờ cũng nhận giá trị từ một cái gì đó (biến, hằng, item, range v.v...) nên không bao giờ gặp lỗi! Nhờ bài này của Kyo thì nay em sẽ chú ý hơn khi ghi trực tiếp con số cụ thể để tính toán. Đúng là mọi thứ đều có giới hạn hoặc quy luật của nó bắt buộc ta phải tuân theo nếu ta tham gia cuộc chơi.
"hằng" của bạn là cái gì?
Hiện tượng tràn số, quy luật mặc định kiểu và ép kiểu là căn bản của lập trình. Bước vào công việc tính toán số không thể nào làm ngơ nó được.
Bạn may mắn là chưa bị bể mánh bởi câu "On Error..." đi đôi với vào tràn số.

Chú cho các bạn khác:
Đã muón tính toán bằng lập trình thì bắt buộc phải biết sơ qua về cấu trúc căn bản của Integer.
Căn bản integer dùng 16 bits; 15 bits đầu biểu hiện dạng nhị phân của số, và bit thứ 16 dùng để xác định âm dương. Tuy nhiên, theo kiến trúc thì số âm không có nghĩa là số dương với cái bit thứ 16. Máy tính dùng quy luật compliment (đảo bit) để biểu hiện số âm. Và hầu như tất cả các máy hiện nay đều theo quy cách 2's compliment.
Như vậy, giới hạn dương của 1 nhị phân 16 bit là gài 15 bit đầu bằng 1, số ấy tương đương với 32767 thập phân.
Để hiểu giới hạn âm, đầu tiên ta tính -32767, tức là 2's compliment của 32767, được biểu hiện ra là bít thứ nhất và 16 bằng 1, các bit khác 0
Thế thì một số có 15 bit đầu là 0 và bit thứ 16 là 1 thì giá trị bao nhiêu? Theo quy luật chung thì nó chính là -32768

Giới hạn của 16 bit unsigned integer là 65535 (tất cả 16 bít đều là 1). Tuy nhiên, VBA không có kiểu Unsigned Integer hay Unsigned Long.
Bạ có thể nhận ra con số này rất gần với số dòng tối đa của Excel 2003. Chính là vì phiên bản Excel này dùng Unsigned 16 bit để làm địa chỉ dòng (0-65535 có 65536 số)
 
Upvote 0
Hai con số -32768 và 32767 này đã là gợi ý tốt nhất về lỗi mà không cần phải tìm hiểu ở bất cứ đâu cả
(không trường lớp, chỉ học mò nên hơi cực vậy đó. Dù sao cũng đến được với.. ngã ba)
Em nói sai thì đừng cốc đầu em nha :
2^15=32768 số nên được tính từ 0 =>32767 và thêm từ -1 =>-32768
Vậy từ -32768 => 32767 sẽ là : -2^15 =>2^15
Mà trang tính Excel cũng lạ : 265 cột =2^8 ; 65536 dòng =2^16
 
Upvote 0
Em nói sai thì đừng cốc đầu em nha :
2^15=32768 số nên được tính từ 0 =>32767 và thêm từ -1 =>-32768
Vậy từ -32768 => 32767 sẽ là : -2^15 =>2^15
Mà trang tính Excel cũng lạ : 265 cột =2^8 ; 65536 dòng =2^16
Vba dùng số bù 2 để biểu diễn, nên nó mới có số âm. Còn cái 2^16 là cách biểu diễn số nguyên không dấu. Tóm lại nó là cách biểu diễn. Nhìn vàO một byte có 8 bit bằng 1, bảo nó là số 255 thì cũng đúng, mà bảo nó là -1 cũng chẳng sai, mà bảo nó là false cũng bình thường.
 
Upvote 0
Em nói sai thì đừng cốc đầu em nha :
2^15=32768 số nên được tính từ 0 =>32767 và thêm từ -1 =>-32768
Vậy từ -32768 => 32767 sẽ là : -2^15 =>2^15
Mà trang tính Excel cũng lạ : 265 cột =2^8 ; 65536 dòng =2^16
Nếu học VBA và bắt đầu nghiên cứu về biến thì bạn sẽ biến giới hạn của Integer là nằm trong khoảng 32,768 đến 32,767, đúng bằng với cặp số bị lỗi mà ta vừa tìm ra
Vậy từ đó ta có thể "đoán": Mỗi khi gõ một con số nào đó thì VBA tự gán nó vào một loại biến nào đó. Chẳng hạn gõ số 17000 thì VBA cho rằng đây là ông Integer. Giờ nhân số 17000 đó cho 2, VBA cũng trả về kết quả là kiểu Integer. Thật không may, 17000*2 = 34000 lại vượt quá giới hạn của Integer
Có thể làm tiếp vài thử nghiệm để kiểm chứng suy đoán
Từ đó rút ra một ghi nhớ quan trọng rằng: Trong VBA hãy cẩn thận với các con số khi mang đi tính toán
 
Upvote 0
Em nói sai thì đừng cốc đầu em nha :
2^15=32768 số nên được tính từ 0 =>32767 và thêm từ -1 =>-32768
Vậy từ -32768 => 32767 sẽ là : -2^15 =>2^15
Mà trang tính Excel cũng lạ : 265 cột =2^8 ; 65536 dòng =2^16
Sai bấy. Tính kiểu vậy là tính kiểu mò ngược.Đọc lại dòng cuối trong bài #31.
Quan niệm cũ, hình như bắt đầu từ IBM, mảng là một cấu trúc khối, chỉ số mảng tính theo phần tử: 1 -> n
Theo quan niệm mới, mảng là một cấu trúc biểu hiện từ phần tử đầu tiên, chỉ số mảng là khoảng cách tương đối giữa phần tử i và phần tử đầu tiên. Vì vậy, phần tử đầu tiên có chỉ số là 0, phần tử thứ i có chỉ số là i-1
Excel 2003 tương tự vậy. A (C[1]) là cột có chỉ số mảng 0, và cột cuối cùng có chỉ số mảng 255. Từ 0 đến 255 có tất cả 256 số, do vậy Excel 2003 có tối đa 256 cột.
Nói tóm lại, Excel 2003 dùng unsigned byte để tính chỉ số cột và unsigned 16 bit integer để tính chỉ số dòng.
Vba dùng số bù 2 để biểu diễn, nên nó mới có số âm. Còn cái 2^16 là cách biểu diễn số nguyên không dấu. Tóm lại nó là cách biểu diễn. Nhìn vàO một byte có 8 bit bằng 1, bảo nó là số 255 thì cũng đúng, mà bảo nó là -1 cũng chẳng sai, mà bảo nó là false cũng bình thường.
Luật số bù 2 này là luật của hệ thống (Windows). Và thường thì hệ thống tuân theo luật của CPU/ALU.
Mấy chục năm về trước thì còn số bù 1. Nhưng hiện nay thì hầu như mọi ngừoi, mọi hệ thống đều đòng ý rằng bù 2 là tốt nhất.
Thường thì số bù 2 (số có âm/dương) được dùng để tính toán, và số trơn (unsigned không có âm/dương) được dùng để làm địa chỉ. Chỉ số mảng cũng là mọt hình thức dịa chỉ.

Chú: câu cuối của bạn - thường thì 1 byte hay một số gì đó, có bất cứ 1 bit nào khác 0 thì nó sẽ được coi như không false (tức là true). Byte có trị unsigned là 255 không thể gọi là false.
False thì dễ nói, nhưng True thì phải coi chừng vì phép AND/OR trong VBA là phép tính bit. Số 1 và 2 cùng khong phải false nhưng 1 AND 2 sẽ cho kết quả là 0 (false), trong khi đó 1 AND 3 sẽ cho kết quả là 1 (không false).
 
Upvote 0
có bất cứ 1 bit nào khác 0 thì nó sẽ được coi như không false
Em nói thế là nói dỡn mà! nhưng chủ yếu là để em đang minh họa cho quan điểm "Tóm lại nó là cách biểu diễn", việc quy định là do mình mà, nếu em sinh sớm khoảng 30 40 chục năm trước, rất có thể em sẽ thay đổi cách biểu diễn true, và false.
Bài đã được tự động gộp:

Mã:
Sub thu()
    Debug.Print TypeName(-9999999999#)
    Debug.Print TypeName(-20000000000#)
    
    Debug.Print TypeName(-2000000000) 'long
    Debug.Print TypeName(-1) 'int
    
    Debug.Print TypeName(255) 'int
    Debug.Print TypeName(32767) 'int
    Debug.Print TypeName(20000000) 'long
    Debug.Print TypeName(2.22222222222222E+22) 'double
    
End Sub

Chạy code trên và tự cảm nhận.
 
Lần chỉnh sửa cuối:
Upvote 0
Sai bấy. Tính kiểu vậy là tính kiểu mò ngược.Đọc lại dòng cuối trong bài #31.
.
Bác nói vậy là tội nghiệp cho con bé này quá.
Khi em viết thì không để ý bài của bác. Khi gởi xong rồi mới thấy .Híc
Em nhớ đã đọc ở đâu đó kiểu Integer,Long hay Double gì đó đều đã được định nghĩa rõ ràng rồi thuộc trong khoảng [a;b] nào đó. Với a,b =2^n nào đó. Lấy theo hệ nhị phân.
 
Upvote 0
Web KT
Back
Top Bottom