Nombor ().

Jennifer Aniston
Lepaskan: 2025-02-15 10:02:12
asal
157 orang telah melayarinya

Number().toFixed() Rounding Errors: Broken But Fixable

Artikel ini pada asalnya diterbitkan di blog David Kaye

Saya mendapati kesilapan bulat dalam nombor (). Menghairankan, pembetulannya sangat mudah. Teruskan membaca ...

mata utama

    Nombor JavaScript (). Kesalahan ini tidak konsisten, dan berlaku apabila bilangan penting terakhir bahagian perpuluhan adalah 5, dan panjang trim hanya dikurangkan oleh satu demi satu di bahagian perpuluhan, dan nilai integer yang berbeza digunakan.
  • Apabila nilai mengandungi perpuluhan, ralat ini boleh ditetapkan dengan menambahkan "1" ke akhir versi rentetan nilai yang akan diubah suai. Penyelesaian ini tidak memerlukan pemeriksaan semula operasi binari atau terapung peringkat rendah.
  • Fungsi tofixed () boleh ditindih menggunakan polyfill sehingga semua pelaksanaan diubahsuai. Walau bagaimanapun, tidak semua orang berpuas hati dengan pendekatan ini. Polyfill melibatkan fungsi prototaip.Tofixed dan memeriksa sama ada panjang hasilnya adalah sifar.

memanaskan

Apabila mengubahsuai fungsi pemformatan digital yang melakukan fungsi yang sama seperti intl.NumberFormat#format (), saya dapati ralat pembulatan dalam versi ini dalam tofixed ().

(1.015).toFixed(2) // 返回 "1.01" 而不是 "1.02"
Salin selepas log masuk
Salin selepas log masuk
Ujian gagal terletak di baris 42 di sini. Saya tidak menemuinya sehingga Disember 2017, yang mendorong saya untuk memeriksa isu -isu lain.

lihat tweet saya mengenai soalan ini:

    Alert ERROR
  • Bandingkan dengan intl.NumberFormat
  • Ringkasan
  • polyfill
Laporan ralat

Terdapat sejarah pelaporan ralat yang panjang apabila menggunakan tofixed ().

Chrome
  • Firefox
  • firefox, lihat juga
  • Internet Explorer
  • Berikut adalah beberapa contoh pendek isu stackoverflow mengenai isu ini:

Contoh 1
  • Contoh 2
  • Biasanya, ini menunjukkan kesilapan nilai
a , Saya mungkin terlepas sesuatu). Ini membolehkan pengaturcara memberi tumpuan kepada isu -isu kecil dan tidak melihat corak yang lebih besar. Saya tidak menyalahkan mereka untuk ini.

Cari corak

Hasil yang tidak dijangka berdasarkan input mesti berasal dari mod dikongsi dalam input. Oleh itu, bukannya mengkaji spesifikasi nombor ().

Fungsi ujian

Saya mencipta fungsi ujian berikut untuk melakukan operasi tofixed () pada satu siri bilangan bulat dari 1 hingga maxValue, menambah perpuluhan seperti .005 kepada setiap integer. Parameter tetap (nombor digit) Tofixed () dikira berdasarkan panjang nilai perpuluhan.

(1.015).toFixed(2) // 返回 "1.01" 而不是 "1.02"
Salin selepas log masuk
Salin selepas log masuk

Penggunaan

Contoh berikut melakukan operasi pada integer 1 hingga 128, menambah perpuluhan .015 untuk setiap integer, dan mengembalikan hasil "tidak dijangka". Setiap hasil mengandungi bidang yang diberikan, dijangkakan, dan sebenar. Di sini kita menggunakan array itu dan mencetak setiap item.

function test({fraction, maxValue}) {
  fraction = fraction.toString()
  var fixLength = fraction.split('.')[1].length - 1

  var last = Number(fraction.charAt(fraction.length - 1))
  var fixDigit = Number(fraction.charAt(fraction.length - 2))

  last >= 5 && (fixDigit = fixDigit + 1)

  var expectedFraction = fraction.replace(/[\d]{2,2}$/, fixDigit)

  return Array(maxValue).fill(0)
    .map(function(ignoreValue, index) {
      return index + 1
    })
    .filter(function(integer) {
      var number = integer + Number(fraction)
      var actual = number.toFixed(fixLength)
      var expected = Number(number + '1').toFixed(fixLength)

      return expected != actual
    })
    .map(function(integer) {
      var number = Number(integer) + Number(fraction)
      return {
        given: number.toString(),
        expected: (Number(integer.toFixed(0)) + Number(expectedFraction)).toString(),
        actual: number.toFixed(fixLength)
      }
    })
}
Salin selepas log masuk

output

Untuk keadaan ini, terdapat 6 hasil yang tidak dijangka.

test({ fraction: .015, maxValue: 128 })
  .forEach(function(item) {
    console.log(item)
  })
Salin selepas log masuk

Discover

Saya dapati ralat ini mengandungi tiga bahagian:

  1. Digit penting terakhir bahagian perpuluhan mestilah 5 (.015 dan .01500 menghasilkan hasil yang sama).
  2. Panjang trim mesti dikurangkan dengan hanya satu bahagian perpuluhan.
  3. Dengan nilai integer yang berbeza yang digunakan, kesilapan berlaku secara tidak konsisten.

tidak konsisten?

Sebagai contoh, untuk integer 1 hingga 128, (nilai). TOFIXED (2) menggunakan 3 tempat perpuluhan yang berbeza yang berakhir pada 5, mengakibatkan hasil berikut:

  • nombor berpintal yang berakhir di .005 selalu gagal (!!)
  • nombor berpintal yang berakhir dalam .015 gagal untuk 1, kemudian 4 hingga 7, kemudian 128
  • nombor berpintal yang berakhir dalam .025 gagal untuk 1, 2, 3, dan kemudian 16 hingga 63
  • nombor berpintal yang berakhir dengan .035 gagal untuk 1, kemudian 32 hingga 128
  • nombor berpintal yang berakhir dengan .045 gagal selama 1 hingga 15, kemudian 128
  • nombor berpintal yang berakhir dengan .055 gagal untuk 1, kemudian 4 hingga 63
  • nombor berpintal yang berakhir dalam .065 gagal untuk 1, 2, 3, kemudian 8 hingga 15, kemudian 32 hingga 128
  • nombor berpintal yang berakhir dalam .075 gagal untuk 1, kemudian 8 hingga 31, kemudian 128
  • nombor berpintal yang berakhir dalam .085 gagal selama 1 hingga 7, kemudian 64 hingga 127 (!!)
  • nombor berpintal yang berakhir dalam .095 gagal untuk 1, kemudian 4 hingga 7, kemudian 16 hingga 128

Mereka yang mengetahui lebih lanjut mengenai matematik titik binari dan terapung daripada yang saya lakukan mungkin dapat menyimpulkan punca utama. Saya meninggalkan ini kepada pembaca sebagai latihan.

Tofixed tetap ()

ditetapkan oleh

lebih daripada satu perpuluhan untuk menetapkan nilai sentiasa dibulatkan dengan betul; Kedua -dua ujian dan polyfill menggunakan pengetahuan ini untuk pemeriksaan ketepatan.

Ini bermakna bahawa semua pelaksanaan tofixed () mempunyai pembetulan mudah: jika nilai mengandungi perpuluhan, tambah "1" ke akhir versi rentetan nilai yang akan diubah suai. Ini mungkin tidak "mematuhi spesifikasi", tetapi ini bermakna kita akan mendapat hasil yang diharapkan tanpa perlu meninjau semula tahap operasi titik binari atau terapung yang lebih rendah.

polyfill

Sebelum semua pelaksanaan diubahsuai, jika anda sanggup melakukan ini (tidak semua orang mahu), anda boleh mengatasi Tofixed () dengan polyfill berikut.

<code>Object { given: "1.015", expected: "1.02", actual: "1.01" }
Object { given: "4.015", expected: "4.02", actual: "4.01" }
Object { given: "5.015", expected: "5.02", actual: "5.01" }
Object { given: "6.015", expected: "6.02", actual: "6.01" }
Object { given: "7.015", expected: "7.02", actual: "7.01" }
Object { given: "128.015", expected: "128.02", actual: "128.01" }</code>
Salin selepas log masuk
kemudian jalankan ujian sekali lagi dan periksa sama ada panjang hasilnya adalah sifar.

(1.005).toFixed(2) == "1.01" || (function(prototype) {
  var toFixed = prototype.toFixed

  prototype.toFixed = function(fractionDigits) {
    var split = this.toString().split('.')
    var number = +(!split[1] ? split[0] : split.join('.') + '1')

    return toFixed.call(number, fractionDigits)
  }
}(Number.prototype));
Salin selepas log masuk
atau hanya menjalankan penukaran awal yang memulakan artikel ini.

test({ fraction: .0015, maxValue: 516 }) // Array []
test({ fraction: .0015, maxValue: 516 }).length // 0
Salin selepas log masuk
Terima kasih kerana membaca!

FAQs (FAQ) pada nombor JavaScript.TOfixed ()

Apakah tujuan nombor.TOfixed () kaedah dalam JavaScript?

Kaedah Number.TOfixed () dalam JavaScript digunakan untuk memformat nombor menggunakan notasi tetap. Ia mengembalikan perwakilan rentetan nombor yang tidak menggunakan perwakilan eksponen dan mempunyai bilangan digit yang ditentukan selepas titik perpuluhan. Sekiranya perlu, nombor itu dibulatkan dan rentetan hasil mempunyai titik perpuluhan selepas panjang.

Kenapa Kaedah Number.TOfixed () kadang -kadang memberikan hasil yang tidak tepat?

Oleh kerana cara JavaScript mengendalikan nombor titik terapung binari, kaedah nombor.tofixed () kadang -kadang memberikan hasil yang tidak tepat. JavaScript menggunakan nombor titik terapung binari, yang tidak dapat mewakili semua perpuluhan perpuluhan dengan tepat. Ketidaktepatan ini boleh membawa kepada hasil yang tidak dijangka apabila bilangannya dibulatkan kepada nombor perpuluhan tertentu menggunakan kaedah Number.TOfixed ().

bagaimana untuk memperbaiki ralat pembulatan dalam number.tOfixed () kaedah?

Satu cara untuk memperbaiki kesilapan pembulatan dalam number.tOfixed () kaedah adalah menggunakan fungsi pembulatan tersuai. Fungsi ini boleh mengambil kira ciri -ciri pemprosesan digital JavaScript dan memberikan hasil yang lebih tepat. Sebagai contoh, anda boleh menggunakan fungsi yang melipatgandakan nombor dengan kuasa 10, mengundurkannya ke integer terdekat, dan membahagikannya dengan kuasa yang sama 10.

Bolehkah saya menggunakan kaedah Number.TOfixed () dengan nilai bukan angka?

tidak, kaedah number.tOfixed () hanya boleh digunakan dengan nilai angka. Jika anda cuba menggunakannya dengan nilai bukan angka, JavaScript akan membuang TypeError. Jika anda perlu menggunakan kaedah ini dengan nilai yang mungkin bukan nombor, anda harus terlebih dahulu menyemak sama ada nilai itu adalah nombor.

Adakah terdapat perbezaan prestasi antara kaedah nombor.tofixed () dan kaedah pembulatan lain?

Perbezaan prestasi antara kaedah

number.tOfixed () dan kaedah pembulatan lain biasanya boleh diabaikan. Walau bagaimanapun, jika anda melakukan banyak operasi, menggunakan fungsi pembulatan tersuai mungkin sedikit lebih cepat daripada menggunakan kaedah Number.TOfixed ().

Bolehkah saya menggunakan kaedah Number.TOfixed () untuk bulat ke lebih daripada 20 tempat perpuluhan?

tidak, kaedah number.tOfixed () hanya boleh bulat ke maksimum 20 tempat perpuluhan. Sekiranya anda cuba melangkah ke lebih daripada 20 tempat perpuluhan, JavaScript akan membuang RangeError.

bagaimana menangani nombor negatif?

number.tOfixed () kaedah mengendalikan nombor negatif dengan cara yang sama seperti berurusan dengan nombor positif. Ia melengkapkan nilai mutlak nombor ke bilangan tempat perpuluhan yang ditentukan dan menambah tanda negatif.

Bolehkah saya menggunakan kaedah Number.TOfixed () dalam semua persekitaran JavaScript?

Ya, kaedah nombor.tOfixed () adalah bahagian standard JavaScript dan harus tersedia dalam semua persekitaran JavaScript. Walau bagaimanapun, kerana enjin JavaScript yang berbeza mengendalikan nombor secara berbeza, hasilnya mungkin tidak sama persis dalam semua persekitaran.

Apa yang berlaku jika saya tidak lulus apa -apa argumen ke kaedah number.tOfixed ()?

Jika anda tidak lulus sebarang argumen ke kaedah nombor. Ini bermakna ia akan mengelilingi nombor ke integer terdekat.

Bolehkah saya menggunakan kaedah Number.TOfixed () dengan jumlah yang besar?

Ya, anda boleh menggunakan kaedah number.tOfixed () dengan bilangan besar. Walau bagaimanapun, ingat bahawa JavaScript hanya boleh mewakili nombor dengan tepat sehingga 2^53 - 1. Jika anda cuba menggunakan kaedah Number.TOfixed () dengan nombor yang lebih besar daripada nombor ini, ia mungkin tidak memberikan hasil yang tepat.

Atas ialah kandungan terperinci Nombor ().. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Artikel terbaru oleh pengarang
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan