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