2013-06-02 16 views
6

Ada için yeni ve sabit nokta "delta" türlerini deniyorum. Özellikle, ben bir 32-bit delta türü aralığı 0.0 .. 1.0 oluşturduk. Bununla birlikte, belirli değerleri kare çizmeye çalıştığımda, bir CONSTRAINT_ERROR alıyorum. Bildiğim kadarıyla, belirtilen aralığımda olmamalı. Bu hatanın eşiği sqrt(1/2) olarak görünüyor. MinGW-w64 sürüm 4.8.0'dan GNAT kullanıyorum.Sabit nokta türü doğru şekilde çarpılmıyor

types.ads:

pragma Ada_2012; 

with Ada.Unchecked_Conversion; 
with Ada.Text_IO; 

package Types is 
    type Fixed_Type is delta 1.0/2**32 range 0.0 .. 1.0 
     with Size => 32; 
    type Modular_Type is mod 2**32 
     with Size => 32; 
    function Fixed_To_Mod is new Ada.Unchecked_Conversion(Fixed_Type, Modular_Type); 
    package MIO is new Ada.Text_IO.Modular_IO(Modular_Type); 
    package FIO is new Ada.Text_IO.Fixed_IO(Fixed_Type); 
end Types; 

specifics.adb:

pragma Ada_2012; 

with Ada.Text_IO; 

with Types; use Types; 

procedure Specifics is 
    package TIO renames Ada.Text_IO; 

    procedure TestValue(val: in Fixed_Type) is 
     square : Fixed_Type; 
    begin 
     square := val * val; 
     TIO.Put_Line("Value " & Fixed_Type'Image(val) & " squares properly."); 
     TIO.Put_Line("Square: " & Fixed_Type'Image(square)); 
     TIO.New_Line; 
    exception 
     when Constraint_Error => 
      TIO.Put_Line("Value " & Fixed_Type'Image(val) & " does not square properly."); 
      TIO.Put_Line("Square: " & Fixed_Type'Image(val * val)); 
      TIO.Put_Line("Not sure how that worked."); 
      TIO.New_Line; 
    end TestValue; 

    function ParseFixed(s: in String; last: in Natural; val: out Fixed_Type) return Boolean is 
     l : Natural; 
    begin 
     FIO.Get(s(s'First..last), val, l); 
     return TRUE; 
    exception 
     when others => 
      TIO.Put_Line("Parsing failed."); 
      return FALSE; 
    end ParseFixed; 

    buffer : String(1..20); 
    last : Natural; 
    f : Fixed_Type; 
begin 
    loop 
     TIO.Put(">>> "); 
     TIO.Get_Line(buffer, last); 
     exit when buffer(1..last) = "quit"; 
     if ParseFixed(buffer, last, f) then 
      TestValue(f); 
     end if; 
    end loop; 
end Specifics; 

Çıktı

Testi kodu (tümü hiçbir uyarı/hata ile gnatmake <file> şeklinde derler) belirli bir sayıya kadar .adb:

>>> 0.1 
Value 0.1000000001 squares properly. 
Square: 0.0100000000 

>>> 0.2 
Value 0.2000000000 squares properly. 
Square: 0.0399999998 

>>> 0.4 
Value 0.3999999999 squares properly. 
Square: 0.1599999999 

>>> 0.6 
Value 0.6000000001 squares properly. 
Square: 0.3600000001 

>>> 0.7 
Value 0.7000000000 squares properly. 
Square: 0.4899999998 

>>> 0.75 
Value 0.7500000000 does not square properly. 
Square: -0.4375000000 
Not sure how that worked. 

>>> quit 

Her nasılsa, val ile çarpma tek başına CONSTRAINT_ERROR ... 'ı açıklayan bir negatif sayı verdi ... ... ama boşver, neden ilk etapta negatif bir sayı alıyorum?

Sonra kare alma numaraları başarısız başladı hangi nokta için test etmeye karar verdi, bu yüzden şu pasajı yazdı:

fixedpointtest.adb:

pragma Ada_2012; 

with Ada.Text_IO; 

with Types; use Types; 

procedure FixedPointTest is 
    package TIO renames Ada.Text_IO; 

    test, square : Fixed_Type := 0.0; 
begin 
    while test /= Fixed_Type'Last loop 
     square := test * test; 
     test := test + Fixed_Type'Delta; 
    end loop; 
exception 
    when Constraint_Error => 
     TIO.Put_Line("Last valid value: " & Fixed_Type'Image(test-Fixed_Type'Delta)); 
     TIO.Put("Hex value: "); 
     MIO.Put(Item => Fixed_To_Mod(test-Fixed_Type'Delta), Base => 16); 
     TIO.New_Line; 
     TIO.Put("Binary value: "); 
     MIO.Put(Item => Fixed_To_Mod(test-Fixed_Type'Delta), Base => 2); 
     TIO.New_Line; 
     TIO.New_Line; 
     TIO.Put_Line("First invalid value: " & Fixed_Type'Image(test)); 
     TIO.Put("Hex value: "); 
     MIO.Put(Item => Fixed_To_Mod(test), Base => 16); 
     TIO.New_Line; 
     TIO.Put("Binary value: "); 
     MIO.Put(Item => Fixed_To_Mod(test), Base => 2); 
     TIO.New_Line; 
     TIO.New_Line; 
end FixedPointTest; 

ve aşağıdaki çıktıyı var:

Last valid value: 0.7071067810 
Hex value: 16#B504F333# 
Binary value: 2#10110101000001001111001100110011# 

First invalid value: 0.7071067812 
Hex value: 16#B504F334# 
Binary value: 2#10110101000001001111001100110100# 

Yani, sqrt(1/2), tekrar karşılaştık. Birisi bana kodumun bunu neden yaptığını açıklayabilir mi? Doğru şekilde çoğaltmak için bir yolu var mı?

+1

Son geçerli ve ilk geçersiz değerlerin * karelerinin * onaltılık ve ikili değerlerini de yazdırmaya değer. Bir hata gibi _feels_ uygulama "başlık altında" bir 32-bit (imzalı) tamsayı kullanarak kısa kesimi alır. Ben delta = 1.0/2 ** 31 ve 1.0/2 ** 33 (aynı aralıkta) denemek için eğimli olurdu. İkincisi daha geniş bir iç türü zorlayabilir veya derlenemez. –

+0

Bu deltaları denedim ve her ikisi de görkemli bir şekilde çalışıyorlar (ikincisi sadece '' Boyut => 32 'yan tümcesini kaldırırken; aksi halde hata derleme). Orijinal delta için, programa ne zaman bir değer atamaya çalıştığımı sorduğumda neden bir "CONSTRAINT_ERROR" yükselttiğini merak ettim. Ancak, -gnato ile yeniden derlediğimde, aslında bu değerleri kabul etmediğini fark ettim. İşaret biti ile uzaklaşmanın bir yolu var mı yoksa farklı bir delta kullanmak zorunda mıyım? – ericmaht

cevap

5

Sanırım "kaputun altında" mevcut olandan 1 tane daha hassaslık istiyorsun. TBMM önyargılı bir temsilini kullandı çünkü

Beyanınız

type Fixed_Type is delta 1.0/2**32 range 0.0 .. 1.0 
     with Size => 32; 

sadece kabul edilir; İşaret biti için yer yok. Bunu, 0.7071067810 en anlamlı bit kümesiyle 16#B504F333# olarak temsil ettiğinden görebilirsiniz. Yani, 0,71 ile 0,71 çarptığınızda, sonuç en anlamlı bit kümesine sahiptir; ve düşük seviyeli kod, bunun işaret biti olması gerektiğini düşünüyor, bu yüzden bir taşma var. Eğer bildirirseniz

Fixed_Type tüm

type Fixed_Type is delta 1.0/2**31 range 0.0 .. 1.0 
     with Size => 32; 

yanı olmalıdır.

bir başka nokta: 0 bir girişi olan specifics davranış raporunuzda.75, sen

>>> 0.75 
Value 0.7500000000 does not square properly. 
Square: -0.4375000000 
Not sure how that worked. 

Ben gnatmake specifics.adb -g -gnato -bargs -E ile yeniden sonucunu alıntı ve sonuç artık

>>> 0.75 
Value 0.7500000000 does not square properly. 

Execution terminated by unhandled exception 
Exception name: CONSTRAINT_ERROR 
Message: 64-bit arithmetic overflow 
Call stack traceback locations: 
0x100020b79 0x10000ea80 0x100003520 0x100003912 0x10000143e 

ve

system__arith_64__raise_error (in specifics) (s-arit64.adb:364) 
__gnat_mulv64 (in specifics) (s-arit64.adb:318) 
specifics__testvalue.2581 (in specifics) (specifics.adb:20)  <<<<<<<<<< 
_ada_specifics (in specifics) (specifics.adb:45) 
main (in specifics) (b~specifics.adb:246) 

ve specifics.adb:20

 TIO.Put_Line("Square: " & Fixed_Type'Image(val * val)); 
olduğu

olarak traceback deşifre

istisna işleyicide, sorunlu kareyi tekrardan içerir (istisna işleyicide yapılacak iyi bir şey değil). 0.75 değerinin yukarıdaki satırda herhangi bir sorun olmadan yazdırıldığını görebilirsiniz:ve son geçerli değere giden eklerde sorun yok.

-gnato'un bu hatayı algıladığını fark ettim, çünkü yalnızca tamsayı aritmetiğine uygulandığını düşünürdüm; ama aslında, GNAT User Guide'da sabit nokta aritmetiği için de geçerli olduğunu belirten bir tartışma var. bir zaman kısıtlı sistem için iyi bir fikir - ama sadece keyfi birden çok hassas aritmetik kullanarak pahasına

>>> 0.75 
Value 0.7500000000 squares properly. 
Square: 0.5625000000 

: Bu sınırlama hatasına önlemek ve -gnato3 kullanarak doğru aritmetik sonucu alabilirsiniz çıkıyor !

+0

Hızlı yanıt için teşekkürler. Tekrar görüşürüz, ama ben yeniyim ve temsilcisi yok. Önerildiği gibi delta değerini değiştirdim ve program olması gerektiği gibi çalışıyor. Neden hiç bir değeri kabul ettiğimi merak ettim> = 0.5. Ben -gnato ile yeniden derledim ve aslında bu değerleri kabul etmediğini fark ettim. – ericmaht

+0

Orijinal deltayı kullanarak -gnato ile yeniden derledim, yani. – ericmaht

İlgili konular