Format Angka IEEE 754

8087 Menyambung kritik tentang floating point, tulisan ini sebagai tanggapan artikel Bernaridho I. Hutabarat (BIH), rubrik Know How PC edisi 06/2009 dengan judul ‘Menguak Misteri Floating Point’. Terdapat beberapa kekeliruan berpikir maupun kedangkalan fakta di situ. Tentu hanya yang menarik perhatian saya.

Pertama, mengenai sejarah IEEE 754. Meskipun publikasi IEEE 754 (draft) baru dilakukan tahun 1985, tetapi sejak tahun 1976 Intel telah merancang math-coprocessor berdasarkan format tersebut [1]. Tak heran karena Intel-lah pemrakarsa standar IEEE 754. Intel sendiri telah memproduksi math-co 8087 pada 1980 (dikenal pula dengan keluarga x87) [2]. Penyerapan standar berlangsung cepat, mengingat pada tahun yang sama (1985) Borland telah mengeluarkan Turbo Pascal 3.02 yang mendukung 8087 [3].

Di majalah, fakta di atas sama sekali tidak tergali oleh BIH. Yang disinggung adalah Intel memproduksi 80386 dan 80387 pada 1987, bahkan keliru menyatakan baru pada Turbo Pascal 6.0 yang memakai 80387. Dengan bermodal Google sebentar saja didapat fakta sejarah lebih akurat.

public class main {
  public static void main (String[] args) {
    float f = 0.1f;
    System.out.printf("Value of %11.9f", f);
  }
}

Lalu mengenai contoh program (di atas) untuk untuk menunjukkan “bukti awal ketidakbenaran mitos” mengenai ketidakcermatan perangkat keras komputer. Ada 3 hal yang menunjukkan kekeliruan berpikir seperti berikut.

  1. Mengapa menggunakan Java ? Rasanya aneh sekali pembuktian perangkat keras dilakukan lewat platform yang bertujuan ‘machine independent’. Format floating point di Java tentu tunduk kepada spesifikasi Java, dengan implementasi di level perangkat keras tergantung interpreter atau virtual machine yang tersedia. Apalagi disebut di bagian lain artikel, “fokus pada Intel dan Windows”. Ingat, semboyan Java adalah “compile once run anywhere”.
  2. BIH melakukan penyimpangan (abuse) dari spesifikasi single-precision, di mana fraction-nya adalah 23-bit (7 s/d 8 digit) [4]. Tentu akan menghasilkan sampah bila dipaksa menjadi 9 digit.
  3. Program di atas lebih menunjukkan bagaimana compiler (atau interpreter) menerjemahkan konstanta (0.1) ke format biner, ketimbang efek dari perangkat keras. Lihat contoh-contoh hasil disassembly di bawah.

Contoh hasil disassembly program Java. Pencetus IEEE 754, William Kahan, pernah membahas masalah serius operasi floating point di Java, “How Java’s floating-point hurts everyone everywhere.” [5]

public static void main(java.lang.String[]);
  Code:
   0:    ldc    #2; //float 0.1f
   2:    fstore_1
   3:    getstatic    #3; //Field java/lang/System.out:Ljava/io/PrintStream;
   6:    ldc    #4; //String Value of %11.9f
   8:    iconst_1
   9:    anewarray    #5; //class java/lang/Object
   12:    dup
   13:    iconst_0
   14:    fload_1
   15:    invokestatic    #6; //Method java/lang/Float.valueOf:(F)Ljava/lang/Float;
   18:    aastore
   19:    invokevirtual    #7; //Method java/io/PrintStream.printf:(Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream;
   22:    pop
   23:    return
}

Contoh hasil disassembly program C (gcc –O2). Tidak terlihat ada operasi floating point di sini, melainkan mencetak langsung angka.

float f = 0.1;
printf ("Value of f is %11.9f\n", f);
80483e1:       c7 44 24 04 00 00 00    mov    DWORD PTR [esp+0x4],0xa0000000
80483e8:       a0
80483e9:       c7 44 24 08 99 99 b9    mov    DWORD PTR [esp+0x8],0x3fb99999
80483f0:       3f
80483f1:       c7 04 24 00 85 04 08    mov    DWORD PTR [esp],0x8048500
80483f8:       e8 df fe ff ff          call   80482dc printf@plt

Contoh disassembly program Pascal (fpc –O2). Di FreePascal dan Delphi operasi floating point menggunakan format double extended sehingga terjadi konversi dari 32-bit (dword ptr) menjadi 80-bit (tbyte ptr). Namun demikian, penentuan konstanta awal tetap dilakukan oleh compiler.

    f := 0.1;
  401381:    a1 40 80 40 00           mov    eax,ds:0x408040
  401386:    a3 00 90 40 00           mov    ds:0x409000,eax
    Writeln(f:1:9);
  40138b:    e8 b0 58 00 00           call   406c40 <fpc_get_output>
  401390:    89 c3                    mov    ebx,eax
  401392:    53                       push   ebx
  401393:    d9 05 00 90 40 00        fld    DWORD PTR ds:0x409000
  401399:    83 ec 0c                 sub    esp,0xc
  40139c:    db 3c 24                 fstp   TBYTE PTR [esp]
  40139f:    b9 01 00 00 00           mov    ecx,0x1
  4013a4:    ba 09 00 00 00           mov    edx,0x9
  4013a9:    b8 00 00 00 00           mov    eax,0x0
  4013ae:    e8 4d 5c 00 00           call   407000 <FPC_WRITE_TEXT_FLOAT>
  4013b3:    e8 38 30 00 00           call   4043f0 <FPC_IOCHECK>
  4013b8:    89 d8                    mov    eax,ebx
  4013ba:    e8 d1 59 00 00           call   406d90 <FPC_WRITELN_END>
  4013bf:    e8 2c 30 00 00           call   4043f0 <FPC_IOCHECK>

Sebagai penutup, pada dasarnya format IEEE 754 mempunyai kompleksitas tersendiri. Perlu pemahaman dan pengujian yang memadai untuk dapat menggunakannya secara benar. Tidak sekedar mengetahui bagaimana format tersebut dibentuk, tetapi juga peran compiler dalam menerjemahkan konstanta dan kode mesin yang dihasilkan. Memahani secara parsial dan pembuktian yang serampangan hanya akan menghasilkan kompleksitas yang tidak perlu.

Referensi

[1] http://www.cs.berkeley.edu/~wkahan/ieee754status/754story.html
[2] http://en.wikipedia.org/wiki/Intel_8087
[3] http://edn.embarcadero.com/article/20792
[4] http://en.wikipedia.org/wiki/Single_precision
[5] http://en.wikipedia.org/wiki/Floating_point

1 Komentar »

  1. Lego Haryanto said

    Cuman komentar di point no. 2, … itu mungkin yg BIH ingin tunjukkan, lho. Mungkin sengaja dibikin banyak decimal digits sehingga hasil print menunjukkan garbage.

    Lalu yang disassembled version dari C code itu, kok ada yg aneh ya. Kemungkinan besar 32-bit float nya itu yg bernilai: 0x3fb99999, tapi itu jadinya bukan 0.1 tapi 1.45.

RSS feed for comments on this post · TrackBack URI

Tinggalkan Balasan ke Lego Haryanto Batalkan balasan