Dijkstra June 2018 | Page 27

Biên dịch đoạn code trên Linux bằng gcc: gcc -o my_prog my_program.cpp -O0 Kết quả khi chạy chương trình: Sum_1 is 0.8999999762 \ Sum_2 is 0.9000000358 Do các số float a, b, c đều là xấp xỉ của 0.2, 0.3, 0.4. Sự khác nhau giữa 2 kết quả đến từ sự khác nhau về thứ tự làm tròn: a = 2 -3 x 1.10011001100110011001101 2 b = 2 -2 x 1.00110011001100110011010 2 c = 2 -2 x 1.10011001100110011001101 2 Trường hợp (a + b) + c: a + b = 2 -1 x 1.0000000000000000000000001000 2 round(a + b) = 2 -1 x 1.00000000000000000000000 2 round(round(a + b) + c) cộng: FMA(X,Y,Z) = round(XY + Z) Một điểm cộng với phép FMA là do chỉ làm tròn 1 lần ở kết quả cuối nên nó sẽ nhanh hơn nếu ta dùng phép nhân và cộng bình thường. Trên thị trường hiện nay đã có các dòng CPU và GPU hỗ trợ phép FMA trực tiếp, đi kèm là các tập lệnh FMA cho một phép tính hoặc cho lệnh SIMD (single instruction, multiple data): • AMD: từ dòng Bulldozer (2011), Piledriver(2012),... • Intel: từ dòng Haswell (2013), Broadwell,... • ARM: ARM Cortex M4F, ARM Cortex A5, ARM Cortex A7,... • NVIDIA: các GPUs có kiến trúc từ Fermi (2010) trở về sau Một ứng dụng quan trọng của FMA, đó là tăng tốc phép tính nhân ma trận: = 2 -1 x 1.11001100110011001100110 2 Trường hợp a + (b + c): b + c = 2 -1 x 1.0110011001100110011001110000 2 round(b + c) = 2 -1 x 1.01100110011001100110100 2 round(a + round(b + c)) = 2 -1 x 1.11001100110011001100111 2 Một ví dụ khác về việc thay đổi phép toán tương đương cũng có thể làm khác kết quả: int main(int agrc, char ** arg) { float a = 0.1f; float sum = 0; for(int i = 0; i < 10; i ++) { sum += a; } float product = a * 10; printf(“Sum is %0.10f \\ Product is %0.10f \n”, sum, product); return 0; } Kết quả sau khi biên dịch: Sum is 1.0000001192 \ Product is 1.0000000000 Ở đây, sum được tính bằng cách cộng với a 10 lần, tức là trong đó có 10 lần làm tròn số. Còn đối với product, kết quả chỉ được làm tròn 1 lần sau phép nhân 10, dẫn đến ít có sai số hơn so với sum. PHÉP TÍNH FUSED MULTIPLY-ADD VÀ ỨNG DỤNG Phép nhân ma trận dùng FMA TRẢ LỜI CÂU HỎI MỞ ĐẦU Đến đây, chắc các bạn cũng đã nắm rõ khái quát floating point là gì, cách nó xấp xỉ một số thực như sao, cũng như sai số tiềm ẩn trong floating point mà chúng ta cần lưu ý khi code. Từ đó chúng ta có thể giải thích câu hỏi được đưa ra đầu bài như sau: a = 2 33 x 1.00101010000001011111001 2 b = 2 33 x 1.001