Có phải tất cả các số ma thuật được tạo ra giống nhau?


77

Trong một dự án gần đây, tôi cần chuyển đổi từ byte sang kilobyte kibibyte . Mã này đủ đơn giản:

var kBval = byteVal / 1024;

Sau khi viết nó, tôi đã làm cho phần còn lại của chức năng hoạt động và chuyển sang.

Nhưng sau này, tôi bắt đầu tự hỏi liệu tôi có nhúng số ma thuật vào mã của mình không. Một phần trong tôi nói rằng nó ổn vì số này là một hằng số cố định và nên dễ hiểu. Nhưng một phần khác của tôi nghĩ rằng nó sẽ cực kỳ rõ ràng nếu được bọc trong một hằng số xác định như thế nào BYTES_PER_KBYTE.

Vì vậy, những con số là hằng số nổi tiếng có thực sự kỳ diệu hay không?


Câu hỏi liên quan:

Khi nào một số là một số ma thuật? Có phải mọi số trong mã được coi là "số ma thuật"? - tương tự, nhưng là những câu hỏi rộng hơn nhiều so với những gì tôi đang hỏi. Câu hỏi của tôi tập trung vào các số không đổi nổi tiếng không được giải quyết trong các câu hỏi đó.

Loại bỏ số ma thuật: Khi nào là lúc nói "Không"? cũng liên quan, nhưng tập trung vào tái cấu trúc trái ngược với việc một số không đổi có phải là số ma thuật hay không.


17
Tôi thực sự đã làm việc trong một dự án nơi họ đã tạo ra các hằng số như thế FOUR_HUNDRED_FOUR = 404. Tôi đã làm việc cho một dự án khác, nơi họ là chiến binh về việc sử dụng các chuỗi không đổi thay vì chữ, vì vậy họ có hàng chục dòng mã trông giống như,DATABASE = "database"
Rob

82
Chắc chắn sử dụng 1024, bởi vì nếu không, nhóm nhà phát triển của bạn sẽ dành tất cả thời gian để tranh luận về thời tiết đó là "kilobytes" hoặc "kibibytes".
Steven Burnap

6
Bạn có thể coi 1024 là kibi và #define KIBIlà 1024, MEBInhư 1024 * 1024,
ysdx

6
@Rob Y: nghe như những lập trình viên Fortran giỏi. Bởi vì ngôn ngữ lập trình đó buộc các lập trình viên phải làm như vậy. Có, có, bạn sẽ thấy hằng như ZERO=0, ONE=1, TWO=2và khi chương trình được chuyển sang ngôn ngữ khác (hoặc các lập trình viên không thay đổi hành vi khi chuyển đổi ngôn ngữ của họ), bạn sẽ thấy nó có quá và bạn phải cầu nguyện mà không bao giờ một người nào đó thay đổi nó để ONE=2...
Holger

4
@NoctisSkytower Nhóm của tôi thích sử dụng các câu lệnh phân chia rõ ràng thay vì các toán tử dịch chuyển bit vì nhiều ngôn ngữ chúng tôi sử dụng và có khả năng triển khai không nhất quán trên các ngôn ngữ đó. Tương tự, các giá trị âm được xử lý không nhất quán với dịch chuyển bitwise. Mặc dù chúng tôi có thể không nhất thiết phải có các giá trị byte âm, chúng tôi chắc chắn có các giá trị âm với các đơn vị đo lường khác mà chúng tôi chuyển đổi.

Câu trả lời:


103

Không phải tất cả các số ma thuật đều giống nhau.

Tôi nghĩ trong trường hợp đó, hằng số đó là OK. Vấn đề với các con số ma thuật là khi chúng là ma thuật, tức là không rõ nguồn gốc của chúng là gì, tại sao giá trị đó là gì, hoặc giá trị đó có chính xác hay không.

Ẩn 1024 phía sau BYTES_PER_KBYTE cũng có nghĩa là bạn không thấy ngay lập tức nếu nó đúng hay không.

Tôi sẽ mong mọi người biết ngay tại sao giá trị là 1024. Mặt khác, nếu bạn đang chuyển đổi byte thành megabyte, tôi sẽ xác định hằng số BYTES_PER_MBYTE hoặc tương tự vì hằng số 1.048.576 không rõ ràng đến mức 1024 ^ 2, hoặc thậm chí còn đúng

Điều tương tự cũng xảy ra đối với các giá trị được quyết định bởi các yêu cầu hoặc tiêu chuẩn, chỉ được sử dụng ở một nơi. Tôi thấy chỉ cần đặt đúng hằng số với một nhận xét cho nguồn có liên quan để dễ xử lý hơn là xác định nó ở nơi khác và phải đuổi cả hai phần xuống, ví dụ:

// Value must be less than 3.5 volts according to spec blah.
SomeTest = DataSample < 3.50

Tôi thấy tốt hơn

SomeTest = DataSample < SOME_THRESHOLD_VALUE

Chỉ khi SOME_THRESHOLD_VALUEđược sử dụng ở nhiều nơi, sự đánh đổi mới trở nên đáng giá để xác định một hằng số, theo ý kiến ​​của tôi.


67
"Vấn đề với những con số kỳ diệu là khi họ đang kỳ diệu" - Đây là ví dụ một lời giải thích tuyệt vời của khái niệm đó! Tôi đang nghiêm túc đấy! +1 cho câu đó một mình.
Jörg W Mittag

20
Đây là một điều tôi vừa nghĩ ra: "không phải con số đó là vấn đề, đó là phép màu."
Jörg W Mittag

10
1024 là hiển nhiên đối với ai? Không phải đó là lời biện minh cho mọi con số ma thuật sao? Tất cả các số ma thuật được sử dụng bởi vì chúng rõ ràng cho bất cứ ai đã viết chúng. Không phải là 9,8 cũng rõ ràng? Đối với tôi, rõ ràng đó là sự gia tốc của trọng lực trên trái đất, nhưng dù sao tôi cũng sẽ tạo ra một hằng số, bởi vì những gì rõ ràng đối với tôi có thể không rõ ràng đối với người khác.
Tulains Córdova 17/12/14

15
Không. Một nhận xét giống như bình luận trong ví dụ "tốt hơn" của bạn là một lá cờ đỏ khổng lồ. Đó là mã thậm chí không vượt qua bài kiểm tra khả năng đọc của người viết nó vào thời điểm đó. Tôi sẽ đưa ra một ví dụ. e^i*pi = -1rõ ràng hơn (tốt hơn) hơn 2.718^i*3.142 = -1. Các biến số quan trọng và chúng không chỉ dành cho mã thông thường. Mã được viết để đọc đầu tiên, biên dịch thứ hai. Ngoài ra, thông số kỹ thuật thay đổi (rất nhiều). Trong khi 1024 có lẽ không nên cấu hình, âm thanh 3.5 sẽ như vậy.
Nathan Cooper

51
Tôi cũng sẽ không sử dụng hằng số cho 1024 ^ 2; 1024*1024làm ơn
Các cuộc đua nhẹ nhàng trong quỹ đạo

44

Có hai câu hỏi tôi đặt ra khi nói về số ma thuật.

Số có tên không?

Tên rất hữu ích vì chúng ta có thể đọc tên và hiểu mục đích của số đằng sau nó. Các hằng số đặt tên có thể tăng khả năng đọc nếu tên dễ hiểu hơn số mà nó thay thế tên hằng là ngắn gọn.

Rõ ràng, các hằng số như pi, e, et al. có tên ý nghĩa. Một giá trị như 1024 có thể là BYTES_PER_KBnhưng tôi cũng mong rằng bất kỳ nhà phát triển nào cũng sẽ biết 1024 có nghĩa là gì. Đối tượng dự định cho mã nguồn là các lập trình viên chuyên nghiệp, những người cần có kiến ​​thức nền tảng để biết nhiều quyền hạn khác nhau và lý do tại sao chúng được sử dụng.

Nó được sử dụng ở nhiều địa điểm?

Trong khi tên là một thế mạnh của hằng số, một cái khác là tái sử dụng. Nếu một giá trị có khả năng thay đổi, nó có thể được thay đổi ở một nơi thay vì cần phải săn lùng nó ở nhiều địa điểm.

Câu hỏi của bạn

Trong trường hợp câu hỏi của bạn, tôi sẽ sử dụng số nguyên trạng.

Tên: có một tên cho số đó, nhưng nó không thực sự hữu ích. Nó không đại diện cho hằng số toán học hoặc giá trị được chỉ định trong bất kỳ tài liệu yêu cầu nào.

Địa điểm: ngay cả khi được sử dụng ở nhiều địa điểm, nó sẽ không bao giờ thay đổi, phủ nhận lợi ích này.


1
Lý do sử dụng hằng số thay vì số ma thuật không chỉ vì số lượng nói sẽ thay đổi, đó cũng là để dễ đọc và tự viết tài liệu.
Tulains Córdova 17/12/14

4
@ user61852: hằng số được đặt tên không phải lúc nào cũng dễ đọc hơn. Họ thường xuyên, nhưng không phải luôn luôn.
whatsisname 17/12/14

2
Cá nhân, tôi sử dụng hai câu hỏi này thay vào đó: "Giá trị này có bao giờ thay đổi trong vòng đời của chương trình không?" và "Các nhà phát triển mà tôi mong đợi được làm việc trên phần mềm này có hiểu con số này để làm gì không?"
Steven Burnap

4
Ý bạn là vấn đề Y2K? Tôi không chắc nó có liên quan ở đây. Vâng, có rất nhiều mã như 'ngày - 1900', nhưng trong mã đó, rắc rối không phải là số ma thuật "1900".
Steven Burnap

1
Câu trả lời này có thể có lợi từ một đề cập, rằng một số số "rõ ràng", 1024 chắc chắn là một, là do các nhà phát triển khác rất có thể tự viết chúng thành số, ngay cả khi ai đó định nghĩa một hằng số được đặt tên cho chúng. Đối với một người rất có thể thậm chí sẽ không nghĩ đến việc tìm kiếm mã nguồn cho hằng số hiện tại cho 1024 nếu tôi chưa biết có một cái, nếu tôi cần sử dụng chuyển đổi số lượng 1024 byte.
hyde

27

Trích dẫn này

Đó không phải là con số đó là vấn đề, đó là phép màu.

như đã nói bởi Jörg W Mittag trả lời câu hỏi này khá tốt.

Một số con số đơn giản là không có phép thuật trong một bối cảnh cụ thể. Trong ví dụ được cung cấp trong câu hỏi, các đơn vị đo được chỉ định bởi các tên biến và thao tác đang diễn ra khá rõ ràng.

Vì vậy, 1024không có gì kỳ diệu bởi vì bối cảnh làm cho nó rất rõ ràng rằng đó là giá trị phù hợp, không đổi để sử dụng cho các chuyển đổi.

Tương tự như vậy, một ví dụ về:

var numDays = numHours / 24; 

cũng rõ ràng và không kỳ diệu bởi vì nó được biết rằng có 24 giờ trong ngày.


21
Nhưng ... nhưng ... 24 có thể thay đổi! Trái đất đang quay chậm và cuối cùng sẽ có 25 giờ! (Tất nhiên tất cả chúng ta sẽ chết vào lúc đó, khiến việc bảo trì phần mềm đó trở thành vấn đề của người khác)

14
Điều gì xảy ra sau khi phần mềm của bạn được triển khai trên Sao Hỏa? Bạn nên tiêm liên tục ...
durron597 17/12/14

8
@ durron597: nếu chương trình của bạn chạy đủ lâu để trái đất chậm lại trong thời gian đó . Bạn không nên tiêm hằng số, thay vào đó là một hàm chấp nhận dấu thời gian (mặc định là bây giờ) và trả về số giờ trong ngày mà dấu thời gian rơi ;-)
Steve Jessop

13
Bạn sẽ cần học YAGNI.
whatsisname 18/12/14

3
@ durron597 Không có gì đặc biệt xảy ra khi phần mềm chấm công của bạn được triển khai trên Sao Hỏa, bởi vì theo quy ước, ngày Sao Hỏa dài 24 giờ, nhưng mỗi giờ dài hơn 2,7% so với trên Trái đất . Tất nhiên, không phải ngày thiên văn Trái đất hay ngày mặt trời Trái đất chính xác là 24 giờ (con số chính xác nằm trên cùng một trang), vì vậy 24 dù sao bạn cũng không thể sử dụng ! Giống như Izkata đã đề cập, bước nhảy vọt đau đớn. Có lẽ bạn sẽ may mắn hơn khi sử dụng hằng số 24trên Sao Hỏa hơn là trên Trái đất!
một CVn

16

Các áp phích khác đã đề cập rằng việc chuyển đổi xảy ra là 'rõ ràng', nhưng tôi không đồng ý. Câu hỏi ban đầu, tại thời điểm này, bao gồm:

kilobyte kibibytes

Vì vậy, tôi đã biết tác giả là hoặc đã nhầm lẫn. Trang Wikipedia thêm vào sự nhầm lẫn:

1000 = KB kilobyte (metric)
1024 = kB kilobyte (JEDEC)
1024 = KiB kibibyte (IEC)

Vì vậy, "Kilobyte" có thể được sử dụng để chỉ cả hai yếu tố 1000 và 1024, với sự khác biệt duy nhất về tốc ký là cách viết hoa của 'k'. Trên hết, 1024 có thể có nghĩa là kilobyte (JEDEC) hoặc kibibyte (IEC). Tại sao không phá vỡ tất cả sự nhầm lẫn đó hoàn toàn với một hằng số với một tên có ý nghĩa? BTW, chủ đề này đã sử dụng "BYTES_PER_KBYTE" thường xuyên và điều đó không kém phần mơ hồ. KBYTE: đó là KIBIBYTE hay KILOBYTE? Tôi muốn bỏ qua JEDEC và có BYTES_PER_KILOBYTE = 1000BYTES_PER_KIBIBYTE = 1024. Không còn nhầm lẫn.

Lý do tại sao những người như tôi, và nhiều người khác ở ngoài đó, có 'chiến binh' (để trích dẫn một người bình luận ở đây) ý kiến ​​về việc đặt tên số ma thuật là tất cả về tài liệu những gì bạn định làm và xóa bỏ sự mơ hồ. Và bạn thực sự đã chọn một đơn vị đã dẫn đến rất nhiều nhầm lẫn.

Nếu tôi thấy:

int BYTES_PER_KIBIBYTE = 1024;  
...  
var kibibytes = bytes / BYTES_PER_KIBIBYTE;  

Sau đó, rõ ràng ngay lập tức những gì tác giả dự định làm, và không có sự mơ hồ. Tôi có thể kiểm tra hằng số trong vài giây (ngay cả khi nó nằm trong một tệp khác), vì vậy mặc dù nó không phải là 'tức thời', nhưng nó đủ gần với tức thì.

Cuối cùng, nó có thể rõ ràng khi bạn viết nó, nhưng nó sẽ ít rõ ràng hơn khi bạn quay lại sau đó, và nó thậm chí có thể ít rõ ràng hơn khi người khác chỉnh sửa nó. Phải mất 10 giây để tạo ra một hằng số; Có thể mất nửa giờ hoặc hơn để gỡ lỗi một vấn đề với các đơn vị (mã sẽ không nhảy ra khỏi bạn và cho bạn biết các đơn vị sai, bạn sẽ phải tự mình làm toán để tìm ra điều đó, và bạn có thể sẽ săn lùng 10 con đường khác nhau trước khi bạn kiểm tra các đơn vị).


2
Câu trả lời tốt. Nó sẽ mạnh hơn nếu bạn tính đến văn hóa nhóm cá nhân. Nếu bạn tin rằng hồ sơ SE của tôi , tôi đủ tuổi để đạt được các tiêu chuẩn cụ thể đó. Vì vậy, sự nhầm lẫn duy nhất đến từ "thuật ngữ tiêu chuẩn hiện tại (không phải) là gì?" Và bạn có thể an toàn khi cho rằng tôi làm việc với một nhóm khủng long đồng nghiệp, những người có cùng khó khăn về thuật ngữ (không).

@ GlenH7: IMHO, các đơn vị năng lượng hai nên được giữ lại để lưu trữ, vì nó được phân bổ trong các khối có kích cỡ bằng hai. Kích thước phân bổ tối thiểu là 4096 byte, có ý nghĩa hơn không khi có một đơn vị cho dung lượng lưu trữ cần thiết để chứa 256 tệp có kích thước tối thiểu hoặc dung lượng lưu trữ cần thiết để giữ các tệp như vậy là 244.140625? Cá nhân, tôi thấy sự khác biệt giữa megabyte của nhà sản xuất ổ cứng và các megabyte khác tương tự như sự khác biệt giữa inch đường chéo đặt TV và inch đường chéo thực.
supercat

@Ryan: Đối với trường hợp cụ thể này, tôi muốn nói về việc áp dụng các đơn vị tiêu chuẩn - KB là 1000 byte hoặc mã sai và 1024 byte là KiB hoặc mã sai. Đây là cách duy nhất chúng ta sẽ vượt qua được vấn đề "đơn vị là mơ hồ". Những người khác nhau định nghĩa "hằng số ma thuật" (như KB) khác nhau sẽ không giúp ích gì.
Brendan

11

Việc xác định tên khi đề cập đến một giá trị số cho thấy rằng bất cứ khi nào cần một giá trị khác ở một nơi sử dụng tên đó, thì tất cả sẽ cần thiết. Nó cũng có xu hướng đề xuất rằng thay đổi giá trị số được gán cho tên là một cách hợp pháp để thay đổi giá trị. Hàm ý như vậy có thể hữu ích khi nó đúng và nguy hiểm khi nó sai.

Việc hai nơi khác nhau sử dụng một giá trị theo nghĩa đen cụ thể (ví dụ 1024) sẽ gợi ý một cách yếu ớt rằng những thay đổi sẽ khiến lập trình viên thay đổi một phần nào đó có khả năng truyền cảm hứng cho lập trình viên muốn thay đổi người khác, nhưng hàm ý đó yếu hơn nhiều so với áp dụng nếu lập trình viên gán tên cho một hằng số như vậy.

Một mối nguy hiểm lớn với một cái gì đó giống như #define BYTES_PER_KBYTE 1024nó có thể gợi ý cho ai đó gặp phải printf("File size is %1.1fkB",size*(1.0/BYTES_PER_KBYTE));rằng một cách an toàn để làm cho mã sử dụng hàng ngàn byte sẽ là thay đổi #definecâu lệnh. Tuy nhiên, một sự thay đổi như vậy có thể là thảm họa, ví dụ, nếu một số mã không liên quan khác nhận được kích thước của một đối tượng trong Kbytes và sử dụng hằng số đó khi phân bổ bộ đệm cho nó.

Có thể hợp lý khi sử dụng #define BYTES_PER_KBYTE_FOR_USAGE_REPORT 1024#define BYTES_PER_KBYTE_REPORTED_BY_FNOBULATOR 1024, gán một tên khác cho mọi mục đích khác nhau được phục vụ bởi hằng số 1024, nhưng điều đó sẽ dẫn đến nhiều định danh được xác định và sử dụng chính xác một lần. Hơn nữa, trong nhiều trường hợp, thật dễ hiểu giá trị nghĩa là gì nếu người ta nhìn thấy mã nơi nó được sử dụng và dễ nhất là tìm ra mã có nghĩa là gì nếu người ta thấy giá trị của bất kỳ hằng số nào được sử dụng trong đó. Nếu một chữ số chỉ được sử dụng một lần cho một mục đích cụ thể, thì việc viết chữ ở nơi được sử dụng thường sẽ mang lại mã dễ hiểu hơn là gán nhãn cho nó ở một nơi và sử dụng giá trị của nó ở một nơi khác.


7

Tôi sẽ nghiêng về việc chỉ sử dụng số, tuy nhiên tôi nghĩ một vấn đề quan trọng chưa được đưa ra: Cùng một số có thể có nghĩa là những điều khác nhau trong các bối cảnh khác nhau và điều này có thể làm phức tạp hóa việc tái cấu trúc.

1024 cũng là số lượng KiB trên mỗi MiB. Giả sử chúng ta sử dụng 1024 để thể hiện tính toán đó ở đâu đó hoặc ở nhiều nơi và bây giờ chúng ta cần thay đổi nó để tính GiB thay thế. Thay đổi hằng số dễ dàng hơn tìm kiếm / thay thế toàn cầu nơi bạn có thể vô tình thay đổi sai ở một số nơi hoặc bỏ lỡ nó ở những nơi khác.

Hoặc thậm chí có thể là một mặt nạ bit được giới thiệu bởi một lập trình viên lười biếng cần được cập nhật một ngày.

Đó là một ví dụ giả định nhưng trong một số cơ sở mã, điều này có thể gây ra sự cố khi tái cấu trúc hoặc cập nhật cho các yêu cầu mới. Trong trường hợp cụ thể này, tôi sẽ không coi số đơn giản là dạng thực sự xấu, đặc biệt nếu bạn có thể đưa phép tính vào một phương thức để sử dụng lại, tôi có thể sẽ tự làm điều đó nhưng xem xét hằng số 'chính xác hơn'.

Nếu bạn sử dụng các hằng số được đặt tên, như supercat nói, điều quan trọng là phải xem xét liệu bối cảnh có quan trọng không, và nếu bạn cần nhiều tên.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.