· kaynak Hacker News – Front Page (native)
IEEE'in VHDL float paketi sqrt(2)'yi yanlış yuvarlıyor, simülatörleri çökertiyor; GHDL ve NVC etkileniyor
Bir denetim, IEEE 1076 VHDL float paketinin çift duyarlıklı sqrt(2) değerini bir ULP düşük döndürdüğünü, paketin tamamında yanlış yuvarlama, hatalı çarpma-toplama sonuçları ve simülatör çökmeleri bulunduğunu ortaya koydu; GHDL ve NVC dosyayı değiştirmeden dağıtıyor.
VHDL standardıyla birlikte gelen floating-point paketi, çift duyarlıklı biçimde sqrt(2.0) değerini doğru yuvarlanmış sonucun bir birim altında (son basamakta bir ULP düşük) hesaplıyor; bu bulgu, bir GitHub deposunda yayımlanan ve Hacker News'in ana sayfasında paylaşılan bir denetim raporunda açıklandı. Hatalı dosya IEEE'nin kendisi tarafından IEEE Std 1076-2008 ve 1076-2019 kapsamında yayımlandığı için hata, paketi değiştirmeden dağıtan GHDL ve NVC üzerinden kullanıcılara ulaşıyor.
Ana kusur
Paketin kendi varsayılanları altında — en yakına yuvarlama, üç guard biti, denormal desteği açık — denetime göre float64'te sqrt(2.0) işlemi 3FF6A09E667F3BCC onaltılık değerini üretirken, tam yuvarlanmış doğru cevap 3FF6A09E667F3BCD. float32'deki sqrt(14.0) da aynı şekilde bir bit düşük. Denetim sorunun kaynağını, ilk bir kök değerini sabit sayıda Newton adımıyla iyileştiren ve sticky bit tutmayan bir uygulamaya bağlıyor; sonuç biti başına bir çıkarma işlemi yapan, kâğıt üzerinde el ile kök alma yöntemiyle aynı prosedür olan basamak-basamağın ise yapısal olarak tam doğru olduğunu belirtiyor.
Hatayı tetiklemek için sıra dışı girdiler de gerekmiyor. 1'den 4.096'ya kadar olan tam sayıların arasında denetim, float32 karekökü bir birim hatalı çıkan 256 değer ve float64 sonucu hatalı çıkan 264 değer sayıyor. 4.913 küçük çarpma-toplama üçlüsünden 290'ı yanlış float32 sonucu döndürüyor.
Sonuçlar nasıl doğrulandı
Denetimin referans uygulaması her işleneni bir işaret artı tam kesirli bir sayı olarak ele alıyor, her işlemi tam olarak gerçekleştiriyor ve çağrının belirttiği yuvarlama biçiminde hedef formata yalnızca bir kez yuvarlıyor. Simülatör çıktısı bu referansla karşılaştırılıyor.
Kasıtlı olarak küçük tutulan 7 bitlik bir formatta (üç üstel bit, üç kesirli bit) her işlenen, işlenen çifti ve çarpma-toplama üçlüsü tek tek sayıldı: 8.692.326 çağrının 1.271.619'u doğru yuvarlanmamıştı. Daha büyük formatlar, işlem, format ve yuvarlama kipi başına 10.000 hedefli çağrıyla sınandı; bu çağrılar aritmetiğin bozulduğu noktalara — denormaller, üstel aralığının iki ucu, bir sonraki binade'ye yuvarlanan değerler, tam beraberlikler, işaretli sıfırlar ve sonsuzluklar — yönlendirildi. En yakına yuvarlama altında bile mac 10.000 float32 çağrının 2.671'ini, scalb ise 2.653'ünü kaçırıyor. Yönlü yuvarlama kipleri çok daha kötü durumda: round_inf kipinde float32 bölme 10.000'de 2.395 kez, round_zero kipinde çarpma 1.102 kez yanlış. Toplama, çıkarma ve çarpma en yakına yuvarlama altında tam yuvarlanıyor — 10.000 çağrıda sıfır hata — ama yönlü kiplerde onlar da başarısız oluyor.
Son bitten büyük hatalar
Bazı sonuçlar tek bitlik sapma göstermek yerine tümüyle yanlış. mac(-6.0, -1.0, -5.0) tam yuvarlanmış sonuç 1.0 iken -15.0 döndürüyor ve mac(1.0, 1.0, -infinity) -infinity yerine +infinity veriyor. 2.0**-512 değerinin float64'ten float32'ye küçültülmesi 0.0 yerine 1.0 veriyor ve float32'de 2.0127'nin çarpımsal tersi 2.0-127 yerine +infinity döndürüyor. Dönüşümler de sapıyor: to_signed(0.50000006, 32) 1 yerine 0 veriyor ve to_unsigned(199.78125, 8) 200 yerine 199 döndürüyor.
En kötüsü, bazı girdiler aracı tamamen durduruyor. float64'ten float32'ye resize işlemi 2^-1074'ten 2^-1024'e kadar olan iki kuvvetlerinin tümü için +infinity döndürüyor, diğer aralıklarda üsteli 512 kaydırılmış değerler veriyor ve 2^-662 ile 2^-639 arasındaki girdilerde paket gövdesinin 358. satırında index-out-of-bounds hatasıyla simülatörü durduruyor. Denetimin plain.vhd testbench'i yalnızca GHDL'ye ihtiyaç duyuyor: --std=08 ile derlenip çalıştırıldığında 24 satır WRONG yazdırıyor ve 25. çağrı simülasyonu durduruyor.
Yamalar ve üst katman bildirimleri
Denetlenen kaynak, IEEE'nin Packages deposunun release dalından, 1220 revizyonundaki 5.712 satırlık ieee/float_generic_pkg-body.vhdl dosyası. Çalışma grubunun develop dalında aynı baytlar bulunuyor ve denetimin GHDL 7.0.0-dev ile NVC 1.23.1 üzerindeki çalışmaları, her ikisinin aynı vektörleri çalıştırdığı yerlerde özdeş sonuç dosyaları yazdı — araçlar arası uyum, ama yanlış cevaplardaki uyum. Denetim bir yama yayımlıyor; yamadan sonra dört test setindeki 32.470.838 çağrının tümü doğru yuvarlanıyor. Eş sabit noktalı (fixed-point) paket de kurtulamıyor: fixed_pkg bölmesi 751.418 çağrının 41.026'sında yanlış ve bunun da kendi yaması var. Bulgular IEEE P1076 çalışma grubuna VHDL-Issues 388 olarak bildirildi ve simülatörlere GHDL issue 3486 ile NVC issue 1758 olarak kaydedildi.
Neden önemli
Bir standardın kendi paketindeki bir kusur, tek bir araçtaki hatadan daha kötüdür: IEEE'nin yayımladığı dosyayı dağıtan her şey onu miras alır ve GHDL'yi NVC ile çapraz doğrulayan mühendisler eşleşen çıktılar görüp makul biçimde sonucun doğru olduğu sonucuna varırdı. Hatalar son bit titizliğinin ötesine geçiyor — çarpma-toplamada işaret hatası, bütün değerler kadar sapmalar ve simülatörü çökerten girdiler var — dolayısıyla float_pkg üzerine kurulu testbench'ler ve aritmetik modeller sessizce yanlış referanslara karşı doğrulama yapıyor olabilir. Denetim aynı zamanda yöntemin değerini de gösteriyor: tam kesirli aritmetik artı minicik bir 7 bitlik formatta kapsamlı test, yıllardır dağıtımda ortaya çıkmayan kusurları gün yüzüne çıkardı ve basamak-basamak kök alma gibi tam yuvarlanmış sonuçların standart bir kütüphanede ulaşılabilir olduğunu kanıtladı.
- #vhdl
- #floating-point
- #ghdl
- #eda
- #standards