2011-05-27 39 views
7

Tôi đang cố tính toán trung bình lăn và để thử và tối ưu hóa một chút, tôi đã đơn giản hóa phép tính để chỉ có một bộ phận. Khi giá trị giảm, có một điểm mà giá trị hiện tại được hạ thấp xuống dưới mức trung bình. Tại thời điểm này nhảy trung bình. Tôi tưởng tượng điều này là bởi vì sự phân chia là unsigned, và bit dấu hiệu của tử số của tôi được hiểu là một số unsigned lớn. Tôi chỉ không chắc chắn nơi tôi cần phải bỏ unsigned để đảm bảo vấn đề này không xuất hiện trở lại.Chỉ số đã ký với số tử chưa ký

unsigned int AverageUsage; 
unsigned int TotalUsage; 
unsigned int incCount; 

    AverageUsage = (TotalUsage - AverageUsage)/++incCount + AverageUsage; 

AverageUsage sẽ luôn là số dương, nhưng khi TotalUsage giảm xuống dưới AverageUsage, tôi không chắc chắn những gì mong đợi với bộ phận

AverageUsage = (signed int)(TotalUsage - AverageUsage)/++incCount + AverageUsage; 

sẽ thiết lập các tử số để ký kết, nhưng tôi không chắc chắn cách phân chia sẽ xảy ra.

AverageUsage = (signed int)((signed int)(TotalUsage - AverageUsage)/++incCount) + AverageUsage; 

Nên làm việc (Tôi có thể đảm bảo kết quả của hoạt động đầy đủ này sẽ không bao giờ âm), nhưng tôi lo lắng về trường hợp incCount đạt giá trị 'trông' âm.

Có một giải pháp đơn giản này mà hy vọng:

  • Không cần một câu lệnh if
  • Không yêu cầu QWORDs

Cảm ơn!

+3

Sẽ hữu ích nếu bạn đưa vào tuyên bố tất cả các biến này. Quy tắc quảng bá của C phụ thuộc vào các loại biểu thức phụ khác nhau. Ví dụ, là AverageUsage một int? int không dấu? unsigned ngắn? v.v. – Nemo

+0

Tôi nghi ngờ mã này; bạn có chắc chắn rằng điều này là chính xác theo số học và tính toán "trung bình cán" chứ không phải là "trung bình tích luỹ"? Một trung bình lăn sẽ yêu cầu một bộ đệm của "giá trị gần đây". – Clifford

+0

@Clifford. Nó là một IIR cơ bản. Có thể bạn đang nghĩ đến FIR tích hợp; tương đương với mẫu thống kê trung bình (chạy/cán). Bất kể, cả hai đều chính xác; dưới dạng bộ lọc thông thấp và xấp xỉ với trung bình dân số. –

Trả lời

4

Bạn có 2 tùy chọn.

Sử dụng Floating Point Math

Tôi nghĩ rằng bạn muốn làm điều này để có được một trung bình thích hợp anyway.

Không có thứ gì như một phân bố nổi/số nguyên hỗn hợp. Vì vậy, cả tử số và mẫu số sẽ được chuyển đổi thành một điểm nổi.

Cho dù tử số hoặc mẫu số được ký hoặc chưa ký thì không quan trọng. Không có điểm nào như dấu phẩy động. Các mẫu số incCount sẽ được chuyển đổi thành một điểm nổi và phân chia điểm nổi đầy đủ sẽ được thực hiện.

bộ phận sử dụng Integer và xử lý các trường hợp đặc biệt

Nếu vì một số lý do bạn muốn ở lại với phân chia số nguyên, sau đó cả hai tử số và mẫu số phải cùng ký/unsigned loại.

Cả Tử số/Mẫu số được ký

incCount sẽ được chuyển đổi sang một số ký. Nếu nó quá lớn thì nó sẽ trông giống như một số âm và câu trả lời của bạn sẽ sai. Bạn phải kiểm tra lỗi tràn này.

Cả Tử số/Mẫu số là unsigned

Bạn phải làm cho tử số unsigned và sử dụng một tuyên bố if() để xử lý hai trường hợp: TotalUsage < AverageUsageTotalUsage > AverageUsage. Ở đây incCount có thể sử dụng đầy đủ các bit số nguyên vì nó sẽ được coi là một số không dấu.

+0

Ok có ý nghĩa. Tôi muốn phân chia số nguyên như tôi đang theo dõi việc sử dụng bộ nhớ (theo byte) mà hầu như luôn ở trong phạm vi 50 + MB. Phân số byte không phải lo lắng. Tôi cũng đang làm việc trên một ARM mà không có FPU. – Gdogg

1

Lưu ý tất nhiên đây không phải là mức trung bình chuẩn. Mức trung bình tiêu chuẩn sẽ là:

Averageusage = TotalUsage/++incCount 

Giả sử (lý tưởng) incCount là một số giá trị gia tăng định kỳ hữu ích (như giây).

Một trung bình phân hủy thường được thực hiện nhiều hơn như: http://donlehmanjr.com/Science/03%20Decay%20Ave/032.htm mà nếu tôi đã dịch một cách chính xác là:

AverageUsage = TotalUsage/(incCount+1) + incCount/(incCount+1) * AverageUsage; 
incCount++; 

Như Himadri đề cập, những có lẽ nên được thực hiện trong nổi điểm số học.

+0

Tôi đã cố gắng giảm thiểu số lượng các bộ phận cần thiết. Công thức của tôi là đơn giản hóa của bạn. – Gdogg

+0

@Gdogg: Trừ khi bạn có một số bằng chứng thực nghiệm cho thấy đây là điểm phát sóng, tôi khuyên bạn nên thực hiện tối ưu hóa sớm. Sử dụng thuật toán chuẩn, chính xác sẽ làm cho người dùng của bạn hạnh phúc hơn vì nó phản ánh đúng những gì mọi người mong đợi khi họ thấy trung bình. –

+0

Việc đơn giản hóa không chỉ là về hiệu suất. Biểu hiện của bạn của nó phá vỡ nặng trong thực tế; '(incCount/(incCount + 1))' luôn bằng 0 trong số học số nguyên. Nếu bạn sắp xếp lại thành '(incCount * AverageUsage)/(incCount + 1)', bạn có nguy cơ tràn trong tử số. –

5

Nguyên tắc chung của ops nhị phân C (bao gồm cả bộ phận) là các toán hạng sẽ cả hai được chuyển đổi sang cùng loại, đó là một trong số: int, unsigned int, long, unsigned long, intmax_t, uintmax_t, float, double, long double . Nếu cả hai toán hạng đều thuộc loại trong danh sách đó, cả hai toán hạng sẽ được chuyển đổi sang loại sau. Nếu không phải là, họ sẽ vừa được chuyển đổi sang int

Vì vậy, trong ví dụ của bạn:

AverageUsage = (signed int)(TotalUsage - AverageUsage)/++incCount + AverageUsage 

nếu incCountunsigned int, sau đó dàn diễn viên của mình không có tác dụng - việc trừ sẽ được chuyển đổi sang ký int và sau đó quay trở lại int chưa được kiểm tra và một bộ phận không dấu sẽ được thực hiện. Nếu bạn muốn một bộ phận có chữ ký, bạn sẽ cần:

AverageUsage = (int)(TotalUsage - AverageUsage)/(int)++incCount + AverageUsage 

mà bạn lưu ý có thể khiến bạn gặp rắc rối nếu số tiền vượt quá INT_MAX.

Nói chung, hướng dẫn bộ vi xử lý chỉ phân chia một loại, được sử dụng cho cả hai toán hạng. Khi có một hướng dẫn đặc biệt để phân chia với các loại khác nhau, nó thường cho một cổ tức lớn hơn (đôi chiều rộng), không phải là một sự ký kết khác nhau.

0

Nếu có thể dự đoán trước và hợp lệ cho TotalUsage < AverageUsage, thì hoàn toàn không phù hợp với các biến này là loại không dấu. TotalUsage < AverageUsage có ngụ ý rằng AverageUsage sau đó có thể là tiêu cực (đó sẽ là kết quả nếu TotalUsage < AverageUsage. Nếu dữ liệu bị 'trung bình' không bao giờ là tiêu cực, sau đó nó là số học không thể cho TotalUsage < AverageUsage đến mức khó tin.

Nếu TotalUsage < AverageUsage không hợp lệ, sau đó cho nó là đúng sẽ chỉ ra một lỗi trong mã của bạn hoặc tràn một số học.Bạn có thể bảo vệ chống lại khả năng đó với một khẳng định, có lẽ một thực hiện như một macro được loại bỏ trong một bản phát hành. Nếu xác nhận xảy ra thì dữ liệu đầu vào không hợp lệ hoặc xảy ra tràn, trong trường hợp sau, loại dữ liệu quá nhỏ và có thể là long long, unsigned long long hoặc double phù hợp.

Ngay cả khi truyền, nếu TotalUsage < AverageUsage là true thì kết quả của biểu thức là số âm, nhưng cuối cùng được gán cho loại không dấu, do đó kết quả sẽ vẫn không chính xác.

Kết luận cuối cùng sau đó là TotalUsage < AverageUsage có thể không bao giờ đúng hoặc dữ liệu của bạn có loại không phù hợp. Giải pháp gần như chắc chắn không phải là loại dàn diễn viên nào.

Lời khuyên của tôi thường là luôn luôn sử dụng loại đã ký cho các biến mà số học sẽ được thực hiện. Điều này là do ngữ nghĩa ngôn ngữ của số học đã ký/không dấu có phần phức tạp và dễ hiểu, và vì các phép toán trung gian có thể tạo ra các giá trị âm khác. Ngay cả khi giá trị âm cho biến là vô nghĩa về mặt ngữ nghĩa, tôi vẫn sẽ ủng hộ việc sử dụng các loại đã ký trong mọi trường hợp, phạm vi tích cực của loại đó vẫn đủ để tránh tràn, và nơi nó không đủ. để sử dụng loại lớn hơn nếu có thể thay vì sử dụng loại không dấu có cùng kích thước. Hơn nữa, khi các phép toán số học trên các loại không dấu là bắt buộc, thì tất cả các toán hạng phải được bỏ dấu (bao gồm cả các chữ), và không có phép toán trung gian nào dẫn đến hoặc tràn.

0

Bạn có thực sự/cần/trung bình cán hay bạn có thể sử dụng một số bộ lọc low-pass khác không? Một đơn cực (đôi khi được gọi là "alpha") bộ lọc có thể phù hợp với bạn:

new_output = alpha * previous_output + (1-alpha)*new_input; 
previous_output = new_output; 

nơi alpha là giữa 0 và 0,9999 ....

càng gần alpha là 1, "chậm" bộ lọc là

Bạn có thể làm điều này trong điểm nổi để dễ dàng hoặc trong các số nguyên khá đơn giản.

Các vấn đề liên quan