Share via


/fp (Tentukan perilaku titik mengambang)

Menentukan bagaimana pengkompilasi memperlakukan ekspresi floating-point, pengoptimalan, dan pengecualian. Opsi /fp menentukan apakah kode yang dihasilkan memungkinkan perubahan lingkungan floating-point ke mode pembulatan, masker pengecualian, dan perilaku subnormal, dan apakah pemeriksaan status floating-point mengembalikan hasil yang saat ini dan akurat. Ini mengontrol apakah kompilator menghasilkan kode yang mempertahankan operasi sumber dan urutan ekspresi, dan sesuai dengan standar untuk penyebaran NaN. Atau, jika sebaliknya menghasilkan kode yang lebih efisien yang dapat menyusun ulang atau menggabungkan operasi dan menggunakan penyederhanaan transformasi aljabar yang tidak diizinkan oleh standar IEEE-754.

Sintaks

/fp:contract
/fp:except[-]
/fp:fast
/fp:precise
/fp:strict

/fp:except[-]
/fp:fast
/fp:precise
/fp:strict

Argumen

/fp:contract

Opsi ini /fp:contract memungkinkan pengkompilasi untuk menghasilkan kontraksi floating-point saat Anda menentukan /fp:precise opsi dan /fp:except . Kontraksi adalah instruksi mesin yang menggabungkan operasi floating-point, seperti Fused-Multiply-Add (FMA). FMA, yang didefinisikan sebagai operasi dasar oleh IEEE-754, tidak membulatkan produk perantara sebelum penambahan, sehingga hasilnya dapat berbeda dari operasi perkalian dan penambahan terpisah. Karena diimplementasikan sebagai satu instruksi, itu bisa lebih cepat daripada instruksi terpisah. Kecepatan datang dengan biaya hasil bitwise yang tepat, dan ketidakmampuan untuk memeriksa nilai perantara.

Secara default, /fp:fast opsi mengaktifkan /fp:contract. Opsi /fp:contract tidak kompatibel dengan /fp:strict.

Opsi /fp:contract ini baru di Visual Studio 2022.

/fp:precise

Secara default, pengkompilasi menggunakan /fp:precise perilaku.

Di bawah /fp:precise, kompilator mempertahankan properti pengurutan ekspresi sumber dan pembulatan kode floating-point saat menghasilkan dan mengoptimalkan kode objek untuk komputer target. Kompiler membulatkan ke presisi kode sumber pada empat titik tertentu selama evaluasi ekspresi: pada penugasan, typecast, ketika argumen floating-point diteruskan ke panggilan fungsi, dan ketika panggilan fungsi mengembalikan nilai floating-point. Komputasi menengah dapat dilakukan pada presisi mesin. Typecast dapat digunakan untuk secara eksplisit membulatkan komputasi perantara.

Kompilator tidak melakukan transformasi aljabar pada ekspresi floating-point, seperti reassosiasi atau distribusi, kecuali dapat menjamin transformasi menghasilkan hasil yang identik bitwise. Ekspresi yang melibatkan nilai khusus (NaN, +infinity, -infinity, -0.0) diproses sesuai dengan spesifikasi IEEE-754. Misalnya, x != x mengevaluasi ke true jika x adalah NaN. Kontraksi floating-point tidak dihasilkan secara default di bawah /fp:precise. Perilaku ini baru di Visual Studio 2022. Versi kompilator sebelumnya dapat menghasilkan kontraksi secara default di bawah /fp:precise.

Kompilator tidak melakukan transformasi aljabar pada ekspresi floating-point, seperti reassosiasi atau distribusi, kecuali dapat menjamin transformasi menghasilkan hasil yang identik bitwise. Ekspresi yang melibatkan nilai khusus (NaN, +infinity, -infinity, -0.0) diproses sesuai dengan spesifikasi IEEE-754. Misalnya, x != x mengevaluasi ke true jika x adalah NaN. Kontraksi floating-point dapat dihasilkan di bawah /fp:precise.

Pengkompilasi menghasilkan kode yang dimaksudkan untuk dijalankan di lingkungan floating-point default. Ini juga mengasumsikan lingkungan floating-point tidak diakses atau dimodifikasi saat runtime. Artinya, ia mengasumsikan kode: membiarkan pengecualian floating-point ditutupi, tidak membaca atau menulis register status floating-point, dan tidak mengubah mode pembulatan.

Jika kode floating-point Anda tidak bergantung pada urutan operasi dan ekspresi dalam pernyataan floating-point Anda (misalnya, jika Anda tidak peduli apakah a * b + a * c dihitung sebagai (b + c) * a atau 2 * a sebagai a + a), pertimbangkan /fp:fast opsi, yang dapat menghasilkan kode yang lebih cepat dan lebih efisien. Jika kode Anda bergantung pada urutan operasi dan ekspresi, dan mengakses atau mengubah lingkungan floating-point (misalnya, untuk mengubah mode pembulatan atau untuk menjebak pengecualian floating-point), gunakan /fp:strict.

/fp:strict

/fp:strict memiliki perilaku yang mirip /fp:precisedengan , yaitu, pengkompilasi mempertahankan properti pengurutan sumber dan pembulatan kode floating-point saat menghasilkan dan mengoptimalkan kode objek untuk komputer target, dan mengamati standar saat menangani nilai khusus. Program ini juga dapat mengakses atau memodifikasi lingkungan floating-point dengan aman saat runtime.

Di bawah /fp:strict, pengkompilasi menghasilkan kode yang memungkinkan program untuk membuka kemasan pengecualian floating-point dengan aman, membaca atau menulis daftar status floating-point, atau mengubah mode pembulatan. Ini membulatkan ke presisi kode sumber pada empat titik tertentu selama evaluasi ekspresi: pada penugasan, typecast, ketika argumen floating-point diteruskan ke panggilan fungsi, dan ketika panggilan fungsi mengembalikan nilai floating-point. Komputasi menengah dapat dilakukan pada presisi mesin. Typecast dapat digunakan untuk secara eksplisit membulatkan komputasi perantara. Kompilator tidak membuat transformasi aljabar pada ekspresi floating-point, seperti reassosiasi atau distribusi, kecuali dapat menjamin transformasi menghasilkan hasil yang identik bitwise. Ekspresi yang melibatkan nilai khusus (NaN, +infinity, -infinity, -0.0) diproses sesuai dengan spesifikasi IEEE-754. Misalnya, x != x mengevaluasi ke true jika x adalah NaN. Kontraksi floating-point tidak dihasilkan di bawah /fp:strict.

/fp:strict secara komputasi lebih mahal daripada /fp:precise karena kompilator harus memasukkan instruksi tambahan untuk menjebak pengecualian dan memungkinkan program untuk mengakses atau memodifikasi lingkungan floating-point saat runtime. Jika kode Anda tidak menggunakan kemampuan ini, tetapi memerlukan pengurutan dan pembulatan kode sumber, atau bergantung pada nilai khusus, gunakan /fp:precise. Jika tidak, pertimbangkan untuk menggunakan /fp:fast, yang dapat menghasilkan kode yang lebih cepat dan lebih kecil.

/fp:fast

Opsi ini /fp:fast memungkinkan pengompilasi untuk menyusun ulang, menggabungkan, atau menyederhanakan operasi floating-point untuk mengoptimalkan kode floating-point untuk kecepatan dan ruang. Pengkompilasi dapat menghilangkan pembulatan pada pernyataan penugasan, typecast, atau panggilan fungsi. Ini dapat menyusun ulang operasi atau membuat transformasi aljabar, misalnya, dengan menggunakan undang-undang asosiatif dan distributif. Ini dapat menyusun ulang kode bahkan jika transformasi tersebut mengakibatkan perilaku pembulatan yang dapat diamati berbeda. Karena pengoptimalan yang ditingkatkan ini, hasil dari beberapa komputasi floating-point mungkin berbeda dari yang dihasilkan oleh opsi lain /fp . Nilai khusus (NaN, +infinity, -infinity, -0.0) mungkin tidak disebarluaskan atau berperilaku ketat sesuai dengan standar IEEE-754. Kontraksi floating-point dapat dihasilkan di bawah /fp:fast. Pengkompilasi masih terikat oleh arsitektur yang mendasar di bawah /fp:fast, dan lebih banyak pengoptimalan mungkin tersedia melalui penggunaan /arch opsi .

Di bawah /fp:fast, kompilator menghasilkan kode yang dimaksudkan untuk dijalankan di lingkungan floating-point default dan mengasumsikan lingkungan floating-point tidak diakses atau dimodifikasi saat runtime. Artinya, ia mengasumsikan kode: membiarkan pengecualian floating-point ditutupi, tidak membaca atau menulis register status floating-point, dan tidak mengubah mode pembulatan.

/fp:fast ditujukan untuk program yang tidak memerlukan pengurutan kode sumber yang ketat dan pembulatan ekspresi floating-point, dan tidak bergantung pada aturan standar untuk menangani nilai khusus seperti NaN. Jika kode floating-point Anda memerlukan pelestarian pengurutan dan pembulatan kode sumber, atau bergantung pada perilaku standar nilai khusus, gunakan /fp:precise. Jika kode Anda mengakses atau memodifikasi lingkungan floating-point untuk mengubah mode pembulatan, membuka kemas pengecualian floating-point, atau memeriksa status floating-point, gunakan /fp:strict.

/fp:except

Opsi ini /fp:except menghasilkan kode untuk memastikan bahwa setiap pengecualian floating-point yang tidak dimasak dinaikkan pada titik yang tepat di mana mereka terjadi, dan bahwa tidak ada pengecualian floating-point lainnya yang dimunculkan. Secara default, /fp:strict opsi mengaktifkan /fp:except, dan /fp:precise tidak. Opsi /fp:except tidak kompatibel dengan /fp:fast. Opsi dapat dinonaktifkan secara eksplisit dengan menggunakan /fp:except-.

Dengan sendirinya, /fp:except tidak mengaktifkan pengecualian floating-point apa pun. Namun, diperlukan program untuk mengaktifkan pengecualian floating-point. Untuk informasi selengkapnya tentang cara mengaktifkan pengecualian floating-point, lihat _controlfp.

Keterangan

Beberapa /fp opsi dapat ditentukan dalam baris perintah pengkompilasi yang sama. Hanya salah /fp:strictsatu opsi , /fp:fast, dan /fp:precise yang dapat berlaku pada satu waktu. Jika Anda menentukan lebih dari salah satu opsi ini pada baris perintah, opsi selanjutnya lebih diutamakan dan pengkompilasi menghasilkan peringatan. Opsi /fp:strict dan /fp:except tidak kompatibel dengan /clr.

Opsi /Za (kompatibilitas ANSI) tidak kompatibel dengan /fp.

Menggunakan direktif kompilator untuk mengontrol perilaku floating-point

Kompilator menyediakan tiga arahan pragma untuk mengambil alih perilaku floating-point yang ditentukan pada baris perintah: float_control, , fenv_accessdan fp_contract. Anda dapat menggunakan arahan ini untuk mengontrol perilaku floating-point pada tingkat fungsi, bukan dalam fungsi. Arahan ini tidak sesuai langsung /fp dengan opsi. Tabel ini menunjukkan bagaimana /fp opsi dan arahan pragma memetakan satu sama lain. Untuk informasi selengkapnya, lihat dokumentasi untuk masing-masing opsi dan arahan pragma.

Opsi float_control(precise, *) float_control(except, *) fenv_access(*) fp_contract(*)
/fp:fast off off off on
/fp:precise on off off off*
/fp:strict on on on off

* Dalam versi Visual Studio sebelum Visual Studio 2022, /fp:precise perilaku default ke fp_contract(on).

Opsi float_control(precise, *) float_control(except, *) fenv_access(*) fp_contract(*)
/fp:fast off off off on
/fp:precise on off off on*
/fp:strict on on on off

* Dalam versi Visual Studio yang dimulai dengan Visual Studio 2022, /fp:precise perilaku default ke fp_contract(off).

Lingkungan titik mengambang default

Saat proses diinisialisasi, lingkungan floating point default diatur. Lingkungan ini menutupi semua pengecualian titik mengambang, mengatur mode pembulatan ke bulat ke terdekat (FE_TONEAREST), mempertahankan nilai subnormal (denormal), menggunakan presisi default significand (mantissa) untuk float, , doubledan long double nilai, dan jika didukung, mengatur kontrol tak terbatas ke mode affine default.

Akses dan modifikasi lingkungan floating-point

Runtime Microsoft Visual C++ menyediakan beberapa fungsi untuk mengakses dan memodifikasi lingkungan floating-point. Ini termasuk _controlfp, _clearfp, dan _statusfp variannya. Untuk memastikan perilaku program yang benar ketika kode Anda mengakses atau memodifikasi lingkungan floating-point, fenv_access harus diaktifkan, baik dengan /fp:strict opsi atau dengan menggunakan fenv_access pragma, agar fungsi-fungsi ini memiliki efek apa pun. Ketika fenv_access tidak diaktifkan, akses atau modifikasi lingkungan floating-point dapat mengakibatkan perilaku program yang tidak terduga:

  • Kode mungkin tidak mematuhi perubahan yang diminta pada lingkungan floating-point,

  • Register status floating-point mungkin tidak melaporkan hasil yang diharapkan atau saat ini,

  • Pengecualian floating-point yang tidak terduga mungkin terjadi atau pengecualian floating-point yang diharapkan mungkin tidak terjadi.

Saat kode Anda mengakses atau memodifikasi lingkungan floating-point, Anda harus berhati-hati saat menggabungkan kode di mana fenv_access diaktifkan dengan kode yang belum fenv_access diaktifkan. Dalam kode di mana fenv_access tidak diaktifkan, pengkompilasi mengasumsikan lingkungan floating-point default platform berlaku. Ini juga mengasumsikan status floating-point tidak diakses atau dimodifikasi. Sebaiknya simpan dan pulihkan lingkungan floating-point lokal ke status defaultnya sebelum kontrol ditransfer ke fungsi yang belum fenv_access diaktifkan. Contoh ini menunjukkan bagaimana float_control pragma dapat diatur dan dipulihkan:

#pragma float_control(precise, on, push)
// Code that uses /fp:strict mode
#pragma float_control(pop)

Mode pembulatan titik mengambang

/fp:precise Di bawah dan /fp:fast, pengkompilasi menghasilkan kode yang dimaksudkan untuk dijalankan di lingkungan floating-point default. Ini mengasumsikan bahwa lingkungan tidak diakses atau dimodifikasi saat runtime. Artinya, kompilator mengasumsikan kode tidak pernah membuka kemasan pengecualian floating-point, membaca atau menulis daftar status floating-point, atau mengubah mode pembulatan. Namun, beberapa program perlu mengubah lingkungan floating-point. Misalnya, sampel ini menghitung batas kesalahan dari perkalian floating-point dengan mengubah mode pembulatan floating-point:

// fp_error_bounds.cpp
#include <iostream>
#include <limits>
using namespace std;

int main(void)
{
    float a = std::<float>::max();
    float b = -1.1;
    float cLower = 0.0;
    float cUpper = 0.0;
    unsigned int control_word = 0;
    int err = 0;

    // compute lower error bound.
    // set rounding mode to -infinity.
    err = _controlfp_s(&control_word, _RC_DOWN, _MCW_RC);
    if (err)
    {
        cout << "_controlfp_s(&control_word, _RC_DOWN, _MCW_RC) failed with error:" << err << endl;
    }  
    cLower = a * b;

    // compute upper error bound.
    // set rounding mode to +infinity.
    err = _controlfp_s(&control_word, _RC_UP, _MCW_RC);
    if (err)
    {
        cout << "_controlfp_s(&control_word, _RC_UP, _MCW_RC) failed with error:" << err << endl;
    }
    cUpper = a * b;

    // restore default rounding mode.
    err = _controlfp_s(&control_word, _CW_DEFAULT, _MCW_RC);
    if (err)
    {
        cout << "_controlfp_s(&control_word, _CW_DEFAULT, _MCW_RC) failed with error:" << err << endl;
    }
    // display error bounds.
    cout << "cLower = " << cLower << endl;
    cout << "cUpper = " << cUpper << endl;
    return 0;
}

Karena kompilator mengasumsikan lingkungan floating point default di bawah /fp:fast dan /fp:precise, bebas untuk mengabaikan panggilan ke _controlfp_s. Misalnya, saat dikompilasi dengan menggunakan dan /O2/fp:precise untuk arsitektur x86, batasan tidak dihitung, dan output program sampel:

cLower = -inf
cUpper = -inf

Saat dikompilasi dengan menggunakan dan /O2/fp:strict untuk arsitektur x86, contoh output program:

cLower = -inf
cUpper = -3.40282e+38

Nilai khusus floating-point

Di bawah /fp:precise dan /fp:strict, ekspresi yang melibatkan nilai khusus (NaN, +infinity, -infinity, -0.0) berperilaku sesuai dengan spesifikasi IEEE-754. Di bawah /fp:fast, perilaku nilai khusus ini mungkin tidak konsisten dengan IEEE-754.

Sampel ini menunjukkan perilaku yang berbeda dari nilai khusus di bawah /fp:precise, , /fp:strictdan /fp:fast:

// fp_special_values.cpp
#include <stdio.h>
#include <cmath>

float gf0 = -0.0;

int main()
{
    float f1 = INFINITY;
    float f2 = NAN;
    float f3 = -INFINITY;
    bool a, b;
    float c, d, e;
    a = (f1 == f1);
    b = (f2 == f2);
    c = (f1 - f1);
    d = (f2 - f2);
    e = (gf0 / f3);
    printf("INFINITY == INFINITY : %d\n", a);
    printf("NAN == NAN           : %d\n", b);
    printf("INFINITY - INFINITY  : %f\n", c);
    printf("NAN - NAN            : %f\n", d);
    printf("std::signbit(-0.0/-INFINITY): %d\n", std::signbit(e));
    return 0;
}

Saat dikompilasi dengan menggunakan /O2 /fp:precise atau /O2 /fp:strict untuk arsitektur x86, output konsisten dengan spesifikasi IEEE-754:

INFINITY == INFINITY : 1
NAN == NAN           : 0
INFINITY - INFINITY  : -nan(ind)
NAN - NAN            : nan
std::signbit(-0.0/-INFINITY): 0

Saat dikompilasi dengan menggunakan /O2 /fp:fast** untuk arsitektur x86, output tidak konsisten dengan IEEE-754:

INFINITY == INFINITY : 1
NAN == NAN           : 1
INFINITY - INFINITY  : 0.000000
NAN - NAN            : 0.000000
std::signbit(-0.0/-INFINITY): 0

Transformasi aljabar titik mengambang

Di bawah /fp:precise dan /fp:strict, pengkompilasi tidak melakukan transformasi matematika kecuali transformasi dijamin menghasilkan hasil yang identik bitwise. Pengkompilasi dapat membuat transformasi seperti itu di bawah /fp:fast. Misalnya, ekspresi a * b + a * c dalam fungsi algebraic_transformation sampel dapat dikompilasi ke dalam a * (b + c) ./fp:fast Transformasi tersebut tidak dilakukan di bawah /fp:precise atau /fp:strict, dan pengkompilasi menghasilkan a * b + a * c.

float algebraic_transformation (float a, float b, float c)
{
    return a * b + a * c;
}

Titik pengecoran eksplisit floating-point

Di bawah /fp:precise dan /fp:strict, pengkompilasi membulatkan ke presisi kode sumber pada empat titik tertentu selama evaluasi ekspresi: pada penugasan, typecast, ketika argumen floating-point diteruskan ke panggilan fungsi, dan ketika panggilan fungsi mengembalikan nilai floating-point. Typecast dapat digunakan untuk secara eksplisit membulatkan komputasi perantara. Di bawah /fp:fast, pengkompilasi tidak menghasilkan cast eksplisit pada titik-titik ini untuk menjamin presisi kode sumber. Sampel ini menunjukkan perilaku di bawah opsi yang berbeda /fp :

float casting(float a, float b)
{
    return 5.0*((double)(a+b));
}

Saat dikompilasi dengan menggunakan /O2 /fp:precise atau /O2 /fp:strict, Anda dapat melihat bahwa jenis cast eksplisit disisipkan di typecast dan pada titik pengembalian fungsi dalam kode yang dihasilkan untuk arsitektur x64:

        addss    xmm0, xmm1
        cvtss2sd xmm0, xmm0
        mulsd    xmm0, QWORD PTR __real@4014000000000000
        cvtsd2ss xmm0, xmm0
        ret      0

Di bawah /O2 /fp:fast kode yang dihasilkan disederhanakan, karena semua jenis cast dioptimalkan:

        addss    xmm0, xmm1
        mulss    xmm0, DWORD PTR __real@40a00000
        ret      0

Untuk mengatur opsi pengkompilasi ini di lingkungan pengembangan Visual Studio

  1. Buka kotak dialog Halaman Properti proyek. Untuk detailnya, lihat Mengatur pengkompilasi C++ dan membuat properti di Visual Studio.

  2. Pilih halaman properti Properti>Konfigurasi C/C++>Code Generation.

  3. Ubah properti Model Titik Mengambang.

Untuk mengatur opsi pengkompilasi ini secara terprogram

Baca juga

Opsi pengkompilasi MSVC
Sintaks baris perintah pengkompilasi MSVC