Bạn đã bao giờ thử tổng hợp tất cả các số từ 1 đến 2.000.000 bằng ngôn ngữ lập trình yêu thích của mình chưa? Kết quả rất dễ dàng để tính toán thủ công: 2.000.001.000.000, lớn hơn 900 lần so với giá trị tối đa của số nguyên 32 bit không dấu.
C # in ra -1453759936
- một giá trị âm! Và tôi đoán Java cũng làm như vậy.
Điều đó có nghĩa là có một số ngôn ngữ lập trình phổ biến bỏ qua Số học tràn theo mặc định (trong C #, có các tùy chọn ẩn để thay đổi điều đó). Đó là một hành vi có vẻ rất nguy hiểm đối với tôi, và đó không phải là sự cố của Ariane 5 do sự cố tràn như vậy sao?
Vì vậy: các quyết định thiết kế đằng sau một hành vi nguy hiểm như vậy là gì?
Biên tập:
Các câu trả lời đầu tiên cho câu hỏi này thể hiện chi phí quá cao của việc kiểm tra. Hãy thực hiện một chương trình C # ngắn để kiểm tra giả định này:
Stopwatch watch = Stopwatch.StartNew();
checked
{
for (int i = 0; i < 200000; i++)
{
int sum = 0;
for (int j = 1; j < 50000; j++)
{
sum += j;
}
}
}
watch.Stop();
Console.WriteLine(watch.Elapsed.TotalMilliseconds);
Trên máy của tôi, phiên bản đã kiểm tra mất 11015ms, trong khi phiên bản không được kiểm tra mất 4125ms. Tức là các bước kiểm tra mất gần gấp đôi thời gian thêm số (tổng cộng gấp 3 lần thời gian ban đầu). Nhưng với 10.000.000.000 lần lặp lại, thời gian thực hiện bằng séc vẫn chưa đến 1 nano giây. Có thể có tình huống đó là quan trọng, nhưng đối với hầu hết các ứng dụng, điều đó không thành vấn đề.
Chỉnh sửa 2:
Tôi đã biên dịch lại ứng dụng máy chủ của chúng tôi (một dịch vụ phân tích dữ liệu Windows nhận được từ một số cảm biến, khá nhiều số bị hỏng) với /p:CheckForOverflowUnderflow="false"
tham số (thông thường, tôi bật kiểm tra tràn) và triển khai trên thiết bị. Giám sát Nagios cho thấy tải CPU trung bình ở mức 17%.
Điều này có nghĩa là lần truy cập hiệu năng được tìm thấy trong ví dụ đã tạo ở trên là hoàn toàn không liên quan đến ứng dụng của chúng tôi.
(1..2_000_000).sum #=> 2000001000000
. Một trong những ngôn ngữ yêu thích của tôi : sum [1 .. 2000000] --=> 2000001000000
. Không yêu thích của tôi : Array.from({length: 2000001}, (v, k) => k).reduce((acc, el) => acc + el) //=> 2000001000000
. (Công bằng mà nói, người cuối cùng là gian lận.)
Integer
trong Haskell là chính xác tùy ý, nó sẽ giữ bất kỳ số nào miễn là bạn không dùng hết RAM phân bổ.
But with the 10,000,000,000 repetitions, the time taken by a check is still less than 1 nanosecond.
đó là một dấu hiệu của vòng lặp được tối ưu hóa. Ngoài ra, câu đó mâu thuẫn với những con số trước đó có vẻ rất hợp lệ đối với tôi.
checked { }
phần để đánh dấu các phần của mã sẽ thực hiện kiểm tra Số học tràn. Điều này là do hiệu suất