Soru Kayan noktalı matematik bozuk mu?


Aşağıdaki kodu göz önünde bulundurun:

0.1 + 0.2 == 0.3  ->  false
0.1 + 0.2         ->  0.30000000000000004

Bu yanlışlıklar neden oluyor?


2268
2018-02-25 21:39


Menşei


Kayan nokta değişkenleri genellikle bu davranışa sahiptir. Donanımda nasıl depolandıklarından kaynaklanır. Daha fazla bilgi için Kayan noktalı sayılarla ilgili Wikipedia makalesi. - Ben S
JavaScript ondalık sayıları şöyle değerlendirir: Kayan nokta sayılarıBu, ekleme gibi işlemlerin yuvarlama hatasına tabi olabileceği anlamına gelir. Bu makaleye bir göz atmak isteyebilirsiniz: Her Bilgisayar Bilimcinin Kayan Nokta Aritmetiği Hakkında Bilmesi Gerekenler - matt b
Sadece bilgi için, javascript'teki TÜM sayısal türler IEEE-754 Çiftler'dir. - Gary Willoughby
@Gary True, 15 haneye kadar olan tamsayılar için kusursuz bir tamsayı kesinliğine sahip olmanıza rağmen, bkz. hunlock.com/blogs/The_Complete_Javascript_Number_Reference - Ender
JavaScript, Math için IEEE 754 standardını kullandığı için, 64 bit yüzen sayılar. Bu, içinde çalışan bilgisayarlara bağlı olarak, kayan nokta (ondalık) hesaplamaları yaparken, hassas hatalara neden olur Temel 2 ondalık ise Temel 10. - Pardeep Jain


Cevaplar:


İkili kayan nokta matematik böyle. Çoğu programlama dilinde, IEEE 754 standardı. JavaScript, Java'nınkiyle aynı olan 64 bit kayan nokta temsilini kullanır. double. Problemin püf noktası, sayıların bu formatta tam sayı olarak ikiye bölünmesidir; rasyonel sayılar (örneğin 0.1, hangisi 1/10) Paydası iki iktidarı olmadığı için tam olarak temsil edilemez.

İçin 0.1 standartta binary64 biçim, temsil tam olarak yazılabilir

  • 0.1000000000000000055511151231257827021181583404541015625 ondalık veya
  • 0x1.999999999999ap-4 içinde C99 hexfloat notasyonu.

Aksine, rasyonel sayı 0.1, hangisi 1/10, tam olarak yazılabilir

  • 0.1 ondalık veya
  • 0x1.99999999999999...p-4 C99 hexfloat notasyonunun bir analogunda, ... 9'ların bitmeyen bir dizisini temsil eder.

Sabitler 0.2 ve 0.3 Programınızda aynı zamanda gerçek değerlerine de yaklaşımlar olacaktır. En yakın olan double için 0.2 rasyonel sayıdan daha büyüktür 0.2 ama en yakın double için 0.3 rasyonel sayıdan daha küçüktür 0.3. Toplamı 0.1 ve 0.2 Rasyonel sayıdan daha büyük rüzgarlar 0.3 ve dolayısıyla kodunuzdaki sabit ile anlaşmaya varmak.

Kayan noktalı aritmetik sorunların oldukça kapsamlı bir tedavisi Her Bilgisayar Bilimcinin Kayan Nokta Aritmetiği Hakkında Bilmesi Gerekenler. Özetlenmesi kolay bir açıklama için bkz. floating-point-gui.de.


1723
2018-04-18 11:52



'Bazı hata sabiti' de Epsilon değeri olarak bilinir. - Gary Willoughby
Sanırım "bazı hata sabiti", "Epsilon" dan daha doğrudur çünkü her durumda kullanılabilecek "Epsilon" diye bir şey yoktur. Farklı durumlarda farklı epsilonlar kullanılmalıdır. Ve makine epsilon hemen hemen hiç kullanmak için iyi bir sabit değildir. - Rotsor
Değil oldukça tüm kayan noktalı matematik IEEE [754] standardına dayanmaktadır. Örneğin, eski IBM onaltılık FP'ye sahip olan bazı sistemler var, ve hala IEEE-754 aritmetiğini desteklemeyen grafik kartları var. Ancak, makul bir yaklaşım için doğrudur. - Stephen Canon
Cray, hız için IEEE-754 uyumluluğunu ortadan kaldırdı. Java, yapışmayı optimizasyon olarak da gevşetti. - Art Taylor
Bence bu hesaplamada para ile ilgili hesaplamalar her zaman, her zaman sabit-nokta aritmetiği ile yapılmalıdır hakkında bir şey eklemelisiniz tamsayılarÇünkü para nicelenir. (Bir kuruşun küçük kesirlerinde veya en küçük para biriminiz ne olursa olsun, iç muhasebe hesaplamaları yapmak mantıklı olabilir. Bu genellikle, örneğin "aylık 29,99 ABD doları" tutarındaki günlük hata oranını düşürme konusunda yardımcı olur. hala sabit nokta aritmetiği.) - zwol


Donanım Tasarımcının Perspektifi

Kayan nokta donanımı tasarladığımdan bu yana donanım tasarımcısının bakış açısını buna eklemem gerektiğine inanıyorum. Hatanın kaynağını bilmek, yazılımda neler olup bittiğini anlamaya yardımcı olabilir ve sonuç olarak, bu durum, kayan nokta hatalarının neden ortaya çıktığını ve zamanla birikir gibi göründüğünü açıklamaya yardımcı olur.

1. Genel Bakış

Bir mühendislik perspektifinden, kayan nokta hesaplamaları yapan donanımın yalnızca bir ünitenin en az bir yarısından azında bir hataya sahip olması gerektiğinden, çoğu kayan nokta işleminin bazı hata unsurları olacaktır. Bu nedenle, bir çok donanım, yalnızca bir ünitenin en az bir yarısından daha az bir hata elde etmek için gerekli olan bir doğrulukta duracaktır. tek işlem Özellikle kayan nokta bölümlerinde sorunludur. Tek bir işlemi oluşturan şey, birimin kaç işlenenine bağlı olduğuna bağlıdır. Çoğu için, bu iki, ancak bazı birimler 3 veya daha fazla işlenen alır. Bu nedenle, hataların zaman içinde toplanmasından dolayı tekrarlanan işlemlerin istenen bir hatayla sonuçlanacağının bir garantisi yoktur.

2. Standartlar

Çoğu işlemci takip eder IEEE-754 standart ama bazı kullanımları denormalized veya farklı standartlar . Örneğin, IEEE-754'te çok küçük kayan nokta sayılarının kesinlik pahasına temsil edilmesine izin veren denormalize bir mod vardır. Bununla birlikte, aşağıdaki tipik çalışma modu olan IEEE-754'ün normalleştirilmiş modunu kapsayacaktır.

IEEE-754 standardında, donanım tasarımcılarının, bir ünitenin en az bir yarısından daha az olduğu sürece herhangi bir hata / epsilon değerine izin verilir ve sonuç yalnızca son bir birimdeki birimin yarısından az olmalıdır. Bir operasyon için yer. Bu, tekrarlanan işlemler olduğunda neden hataların eklendiğini açıklar. IEEE-754 çift duyarlık için bu 54 bittir, çünkü 53 bit, kayan nokta sayısında mantı olarak adlandırılan nümerik kısmı (normalize) temsil etmek için kullanılır (örn. 5.3e5'teki 5.3). Sonraki bölümler, çeşitli kayan nokta işlemlerinde donanım hatasının nedenleri üzerinde daha fazla ayrıntıya girer.

3. Bölümde Yuvarlama Hatasının Nedeni

Kayan nokta bölmesindeki hatanın ana nedeni, bölümü hesaplamak için kullanılan bölüm algoritmalarıdır. Çoğu bilgisayar sistemi, çoğunlukla bir ters ile çarpma kullanarak bölümlemeyi hesaplar. Z=X/Y, Z = X * (1/Y). Bir bölüm, iteratif olarak hesaplanır, yani her bir döngü, istenen hassasiyete ulaşılana kadar, bölümün bazı bitlerini hesaplar. Bu, IEEE-754 için, son yerde bir birimden daha az bir hata ile ilgili bir şeydir. Y (1 / Y) 'nin karşılıklılık tablosu, yavaş bölme içinde bölüm seçim tablosu (QST) olarak bilinir ve bölüm seçim tablosunun bitleri içindeki büyüklük genellikle yarıçapın genişliğidir veya bir kaç bitidir. her yinelemede hesaplanan bölüm artı birkaç koruma biti. IEEE-754 standardı için, çift duyarlıklı (64-bit), bölücünün yarıçapının büyüklüğü, artı birkaç koruyucu uç k, k>=2. Örneğin, bir kerede 2 biti hesaplayan bir bölücü için tipik bir Bölüm Seçme Tablosu (sayı 4) 2+2= 4 bit (artı birkaç isteğe bağlı bit).

3.1 Bölüm Yuvarlama Hatası: Karşılıklı Yaklaşım

Bölüm seçim tablosunda hangi karşılıklı değişkenler var? bölme yöntemi: SRT bölümü ya da Goldschmidt bölümü gibi hızlı bölünme gibi yavaş bölünme; Her giriş, mümkün olan en düşük hatayı elde etmek amacıyla bölünme algoritmasına göre değiştirilir. Her halükarda, tüm karşılıklılıklar yaklaşımları gerçek karşılıklı ve hata bazı unsurları tanıtmak. Hem yavaş bölünme hem de hızlı bölünme yöntemleri, katsayıyı yinelemeli olarak hesaplar; yani, her adımın bir kısmı, her adımda hesaplanır, sonra sonuç, temettüden çıkarılır ve bölücü, hata, birimin yarısından daha az olana kadar, adımları tekrarlar. son sırada birim. Yavaş bölme yöntemleri, her adımda bölümün sabit sayıda basamağını hesaplar ve genellikle daha az maliyetlidir ve hızlı bölme yöntemleri, adım başına değişken sayıda basamak hesaplar ve genellikle daha pahalıdır. Bölme yöntemlerinin en önemli kısmı, çoğunun bir çoğaltma ile tekrarlanan çarpmalara dayanmasıdır. tahmin Karşılıklı olarak, onlar hataya eğilimlidir.

4. Diğer İşlemlerde Yuvarlama Hataları: Kesilme

Tüm işlemlerde yuvarlama hatalarının bir başka nedeni, IEEE-754'ün izin verdiği nihai yanıtın farklı modlarıdır. Sıfırdan, sıfıra doğru, sıfır en yakın (varsayılan), yuvarlak ve yuvarlak. Tüm yöntemler tek bir işlem için en son sırada bir birimden daha az bir hata unsuru oluşturur. Zaman içinde ve tekrarlanan operasyonlarda, budama ayrıca sonuçta meydana gelen hataya kümülatif olarak eklenir. Bu kesme hatası özellikle, tekrarlanan çoğalmanın bir türünü içeren eksponentiasyonda sorunludur.

5. Tekrarlanan Operasyonlar

Kayan nokta hesaplamaları yapan donanımın, sadece tek bir işlem için en son bir ünitenin bir yarısından daha az bir hata ile sonuç vermesi gerektiğinden, izlenmediği takdirde hata tekrarlanan işlemlerde büyür. Sınırlı bir hata gerektiren hesaplamalarda, matematikçilerin yuvarlak-en yakın kullanma yöntemleri gibi yöntemleri kullanmasının nedeni budur. son sırada bile rakam IEEE-754, çünkü zamanla, hataların birbirini iptal etme olasılığı daha yüksektir ve Aralık Aritmetiği varyasyonları ile kombine IEEE 754 yuvarlama modları Yuvarlama hatalarını tahmin etmek ve düzeltmek için. Diğer yuvarlama modlarına göre düşük rölatif hatasından dolayı, en yakın hatta (en sondaki) rakam, IEEE-754'ün varsayılan yuvarlama modudur.

Yuvarlak olarak en yakın olan varsayılan yuvarlama modunu unutmayın. son sırada bile rakamBir operasyon için son bir ünitenin bir yarısından daha azının bir hatayı garanti eder. Kesim, yuvarlama ve yuvarlağın tek başına kullanılması, sondaki bir birimin bir yarısından daha büyük, ancak son yerindeki bir birimden daha az bir hataya neden olabilir, bu nedenle, bu modlar, Aralık Aritmetiğinde kullanılır.

6. Özet

Kısacası, kayan nokta işlemlerinde hataların temel nedeni, donanımdaki kesintinin ve bölünme durumunda bir karşılıklılığın kesilmesinin birleşimidir. IEEE-754 standardı, tek bir işlem için sadece bir ünitenin en az bir yarısından az bir hata gerektirdiğinden, tekrarlanan işlemlerdeki kayan nokta hataları düzeltilmeden eklenecektir.


490
2018-02-25 21:43



(3) yanlış. Bir bölümdeki yuvarlama hatası az değil bir son sırada birim, ama en fazla yarım son sırada bir birim. - gnasher729
@ gnasher729 İyi yakalama. Çoğu temel işlemde, varsayılan IEEE yuvarlama modunu kullanarak en son bir birimden 1 / 2'den daha az hata vardır. Açıklamayı düzenledik ve kullanıcı varsayılan yuvarlama modunu geçersiz kılarsa (bu özellikle gömülü sistemlerde doğrudur) hatanın bir ulp'nin 1 / 2'sinden daha büyük ancak 1'den daha az olabileceğini belirtmiştir. - KernelPanik
(1) Kayan nokta sayılar hatası yok. Her kayan nokta değeri tam olarak ne olduğu. Çoğu (ama hepsi değil) kayan nokta operasyonlar hatalı sonuç vermek. Örneğin, tam olarak 1.0 / 10.0'a eşit bir ikili kayan nokta değeri yoktur. Bazı işlemler (ör. 1.0 + 1.0) yap Diğer taraftan kesin sonuçlar verin. - james large
"Kayan nokta bölümlemede hatanın ana sebebi, bölümleri hesaplamak için kullanılan bölüm algoritmalarıdır" çok Söyleyecek yanıltıcı şey. IEEE-754 uyumlu bir bölüm için bir tek kayan nokta bölmelerinde hata nedeni, sonucun tam olarak sonuç biçiminde temsil edilememesidir; Aynı sonuç, kullanılan algoritmadan bağımsız olarak hesaplanır. - Stephen Canon
@Matt Geç cevap için özür dilerim. Temelde kaynak / zaman sorunları ve değişimlerden kaynaklanıyor. Uzun bölünme / daha 'normal' bölünme yapmanın bir yolu var, ikiye kadar SRT Bölümü deniyor. Bununla birlikte, bu tekrar tekrar bölücüden ayrılır ve çıkarılır ve saat döngüsünde sadece bir bitlik bir sayı hesapladığı için çok sayıda saat döngüsünü alır. Döngü çizelgelerini kullanırız, böylelikle döngü başına daha fazla parçayı hesaplayabiliriz ve etkin performans / hız değişiklikleri yapabiliriz. - KernelPanik


.1 veya 1/10 taban 2'ye (ikili) dönüştürdüğünüzde, ondalık noktadan sonra yinelenen bir desen elde edersiniz, tıpkı üssünde 1 / 3'ü temsil etmeye çalışmak gibi. Değer kesin değildir ve bu nedenle Normal kayan nokta yöntemleri kullanarak tam matematik.


357
2017-11-20 02:39



Büyük ve kısa cevap. Yinelenen desen 0.00011001100110011001100110011001100110011001100110011 ... gibi görünüyor - Konstantin Chernov
Bu, neden daha iyi bir algoritmanın kullanılmadığını açıklamamaktadır ki bu, ilk önce ikili dosyalara dönüştürülmez. - Dmitri Zaitsev
Çünkü performans. İkili kullanarak birkaç bin kat daha hızlıdır, çünkü makine için doğaldır. - Joel Coehoorn
Tam ondalık değerler veren yöntemler vardır. BCD (İkili kodlanmış ondalık) veya ondalık sayıların diğer çeşitli şekilleri. Ancak, bunlar hem daha yavaş (LOT daha yavaş) hem de ikili kayan noktadan daha fazla depolama alanına sahiptir. (örnek olarak, paketlenmiş BCD, bir baytta 2 ondalık basamak depolar. Bu, bir baytın 100 olası değerini saklayabilen bir baytta 100 olası değeri veya bir baytın olası değerlerinin yaklaşık% 60'ını boşaltan 100/256 değerini). - Duncan C
@Jacksonkr hala üs-10'da düşünüyorsun. Bilgisayarlar taban-2'dir. - Joel Coehoorn


Buradaki çoğu cevap, bu soruyu çok kuru ve teknik açıdan ele almaktadır. Bunu normal insanın anlayabileceği şekilde ele almak isterim.

Pizzaları kesmeye çalıştığınızı hayal edin. Pizza dilimlerini kesebilen robotlu bir pizza kesiciniz var. kesinlikle yarısında. Bütün bir pizzayı yarıya indirebilir veya mevcut bir dilimi yarıya indirebilir, ancak her durumda, yarı yarıya her zaman kesin olur.

Bu pizza kesicinin çok ince hareketleri var ve eğer tüm bir pizzayla başlıyorsanız, o zaman bunu yarıya bölün ve her seferinde en küçük dilimi yarıya inmeye devam edin. 53 kez Dilim yüksek hassasiyetli yetenekleri için bile çok küçük. Bu noktada, bu çok ince dilimi artık yarıya bölemezsiniz, ancak içermesi veya içermemesi gerekir.

Şimdi, tüm dilimleri, bir pizzanın onda birine (0.1) veya beşte birine (0,2) katacak şekilde nasıl parçalayacaksınız? Bunu gerçekten düşün ve çalışmayı dene. Elinizde efsanevi hassas bir pizza kesicisine sahipseniz bile gerçek bir pizza kullanmayı deneyebilirsiniz. :-)


En deneyimli programcılar, elbette, gerçek cevabı bilirler. kesin Bu dilimleri kullanarak pizzanın onuncu veya beşinci, ne kadar ince dilerseniz onları dilimleyin. Oldukça iyi bir yaklasim yapabilir ve 0,1 yaklasiminda 0.1 yaklasimini ekliyorsaniz, 0.3'e oldukça iyi bir yaklasim elde edersiniz, ama yine de tam olarak bir yaklasim.

Çifte hassasiyetli sayılar için (pizzanızı 53 kez yarı yarıya düşürmenize olanak veren hassasiyet), 0.1'den daha küçük ve daha büyük olan sayılar 0.09999999999999999167332731531132594682276248931884765625 ve 0.1000000000000000055511151231257827021181583404541015625'dir. İkincisi 0.1'den biraz daha eski olana benzer, bu yüzden sayısal bir ayrıştırıcı, 0.1'lik bir girdi göz önüne alındığında, ikincisini destekleyecektir.

(Bu iki sayı arasındaki fark, en küçük dilim için teknik terim, aşağı yönlü bir önyargıya neden olan, yukarı yönlü bir önyargı tanıtan veya hariç tutmaya karar vermemiz gerektiğine karar vermemiz gereken "en küçük dilim" dir. ulp.)

0.2 durumunda, sayıların hepsi aynıdır, sadece 2 faktörü ile ölçeklenir. Yine, 0.2'den biraz daha yüksek olan değeri tercih ederiz.

Her iki durumda da, 0,1 ve 0,2 için tahminlerin hafif bir yukarı yönlü yanlılığa sahip olduğuna dikkat edin. Bu önyargıları yeterince eklediğimizde, sayıları istediğimiz noktadan daha fazla ileriye doğru iterler ve aslında, 0,1 + 0,2 durumunda, önyargı, sonuçta elde edilen sayının artık en yakın sayı olmaması için yeterince yüksektir. 0.3'e.

Özellikle, 0.1 + 0.2 gerçekten 0.1000000000000000055511151231257827021181583404541015625 + 0.200000000000000011102230246251565404236316680908203125 = 0.3000000000000000444089209850062616169452667236328125, oysa 0.3'e en yakın sayı aslında 0.29999999999999999988897769753748434595763683319091796875'tir.


Not; Bazı programlama dilleri de pizza kesiciler sağlayabilir dilimlerini kesin ondalara bölün. Bu tür pizza kesiciler nadir olmakla birlikte, birisine erişiminiz varsa, bir dilimin tam onda birini veya beşte birini alabilmek için bunu kullanmanız gerekir.

(Aslen Quora'da gönderildi.)


226
2018-02-25 21:41



Tam matematik içeren bazı dillerin olduğunu unutmayın. Bir örnek şemadır, örneğin GNU Guile ile. Görmek draketo.de/english/exact-math-to-the-rescue - Bu matematik kesirler olarak ve sadece sonunda dilimlenir. - Arne Babenhauserheide
@FloatingRock Aslında, çok az ana programlama dilleri dahili rasyonel sayılara sahiptir. Arne bir Schemer'di, benim olduğum gibi, bunlar şımarık olduğumuz şeyler. - Chris Jester-Young
@ArneBabenhauserheide Bence bunun sadece rasyonel sayılar ile çalışacağını eklemenin faydalı olduğunu düşünüyorum. Yani eğer pi gibi irrasyonel sayılarla matematik yapıyorsanız, bunu pi'nin katları olarak saklamanız gerekir. Tabii ki, pi içeren herhangi bir hesaplama tam bir ondalık sayı olarak gösterilemez. - Aidiakapi
@connexo Tamam. Pizza döndürücünüzü 36 dereceye nasıl programlayacaksınız? 36 derece nedir? (İpucu: bunu tam olarak tanımlayabiliyorsanız, ayrıca bir dilim-bir-ondalık pizza kesiciye de sahip olursunuz.) Başka bir deyişle, aslında 1/360 (derece) veya 1 / Sadece ikili kayan nokta ile 10 (36 derece). - Chris Jester-Young
@connexo Ayrıca, "her aptal" bir pizza döndüremez kesinlikle 36 derece. İnsanlar oldukça hassas bir şey yapmak için çok hata yapmaya eğilimlidirler. - Chris Jester-Young


Kayan nokta yuvarlama hataları. 0.1, temel-10'daki temel-10'daki gibi temel-2'de doğru olarak temsil edilemez. Aynen, 1/3, ondalık olarak temsil etmek için sonsuz sayıda basamak alır, ancak taban-3'te "0.1" olur. 0.1, base-2'de, base-10'da olmayan, sonsuz sayıda basamak alır. Ve bilgisayarlarda sonsuz miktarda bellek yok.


199
2018-04-09 12:25



bilgisayarlar 0.1 + 0.2 = 0.3 sağ almak için sonsuz miktarda belleğe ihtiyaç duymaz - Pacerier
@Pacerier Elbette, bir kesriyi temsil etmek için iki sınırsız tamsayıyı kullanabilirler ya da tırnak işareti kullanabilirler. Bu, 'binary' veya 'decimal' kavramını imkansız kılan özel bir kavramdır - bir ikili / ondalık basamak dizisi ve bir yerde, bir radix noktası olduğu fikri. Kesin rasyonel sonuçlar elde etmek için daha iyi bir biçime ihtiyacımız var. - Devin Jeanpierre
@Pacerier: Ne ikili ne de ondalık kayan nokta tam olarak 1/3 veya 1/13 depolayabilir. Ondalık kayan nokta türleri, M / 10 ^ E formunun değerlerini tam olarak temsil edebilir, diğer birçok fraksiyonu temsil etmeye gelince, benzer büyüklükteki ikili kayar nokta sayılarından daha az hassastır. Birçok uygulamada, keyfi kesirler ile daha yüksek hassasiyete sahip olmak, birkaç "özel" olanlarla mükemmel bir hassasiyete sahip olmaktan daha yararlıdır. - supercat
@Pacerier Onlar yap Eğer sayıları ikili floatlar olarak depolarlarsa, bu da cevabın noktasıydı. - Mark Amery
@chux: İkili ve ondalık türleri arasındaki kesinlik farkı çok büyük değildir, ancak ondalık türler için en iyi duruma göre en kötü durumda 10: 1 fark, ikili tiplerle 2: 1 farktan çok daha büyüktür. Kimsenin, donanım veya yazılımda verimli bir uygulama için uygun görünmeyeceğinden, ondalık türlerinden herhangi birinde verimli çalışacak donanım veya yazılı yazılım oluşturup oluşturmadığını merak ediyorum. - supercat


Diğer doğru cevaplara ek olarak, kayan noktalı aritmetik sorunlarından kaçınmak için değerlerinizi ölçeklendirmeyi düşünebilirsiniz.

Örneğin:

var result = 1.0 + 2.0;     // result === 3.0 returns true

... yerine:

var result = 0.1 + 0.2;     // result === 0.3 returns false

İfade 0.1 + 0.2 === 0.3 döner false JavaScript'te, ancak kayan noktadaki neyse ki tamsayı aritmetiği kesin, bu yüzden ondalık gösterim hataları ölçekleme ile önlenebilir.

Pratik bir örnek olarak, doğruluğun en önemli olduğu kayan nokta sorunlarından kaçınmak için tavsiye edilir.1 sent sayısını temsil eden bir tamsayı olarak ele almak için: 2550 yerine sent 25.50 dolar.


1 Douglas Crockford: JavaScript: İyi Parçalar: Ek A - Kötü Parçalar (sayfa 105).


99
2018-02-23 17:15



Sorun, dönüşümün kendisinin yanlış olmasıdır. 16.08 * 100 = 1607.9999999999998. Numarayı bölme ve ayrı ayrı çevirme (16 * 100 + 08 = 1608'de olduğu gibi) başvurmak zorunda mıyız? - Jason
Buradaki çözüm, tüm hesaplamalarınızı tamsayı olarak yapmak, sonra orantılı olarak bölmek (bu durumda 100) ve yalnızca verileri sunarken yuvarlaktır. Bu, hesaplarınızın her zaman kesin olmasını sağlayacaktır. - David Granado
Sadece biraz nitpick için: tamsayı aritmetiği sadece bir noktaya kadar kayan noktaya kadar kesin (pun amaçlı). Sayı, 0x1p53'ten büyükse (Java 7'nin onaltılık kayan nokta notasyonu = 9007199254740992'yi kullanmak için), o zaman bu noktada ulp 2'dir ve 0x1p53 + 1, 0x1p53'e yuvarlanır (ve 0x1p53 + 3, 0x1p53 + 'ya yuvarlanır. 4, yuvarlakdan öte). :-D Ama kesinlikle, numaranız 9 katrilyondan küçükse, iyi olmalısın. :-P - Chris Jester-Young
Peki nasıl oluyor .1 + .2 göstermek için .3? - CodyBugstein
Jason, sonucu sadece bitirmelisin (int) (16.08 * 100 + 0.5) - Mikhail Semenov


Cevabım oldukça uzun, bu yüzden üç bölüme ayırdım. Soru kayan nokta matematiği ile ilgili olduğundan, makinenin gerçekte ne yaptığına vurgu yaptım. Ben de bunu (64 bit) hassasiyete özel yaptım, ama argüman herhangi bir kayan nokta aritmetiğine eşit olarak uygulanır.

önsöz

bir IEEE 754 çift duyarlıklı ikili kayan noktalı format (binary64) sayı formun bir sayısını temsil eder

değer = (-1) ^ s * (1.m51m50... m2m1m0)2 * 2E-1023

64 bitte:

  • İlk bit işaret biti: 1 sayı negatifse, 0 aksi takdirde1.
  • Sonraki 11 bit üs, hangisi dengelemek Başka bir deyişle, iki taraflı bir sayıdan üs bitlerini okuduktan sonra, ikinin gücünü elde etmek için 1023 çıkarılmalıdır.
  • Kalan 52 bit significand (veya mantissa). Mantis içinde 'ima' 1. her zaman2 Herhangi bir ikili değerin en anlamlı bitinden beri ihmal edilir 1.

1 - IEEE 754, bir imzalanmış sıfır - +0 ve -0 farklı tedavi edilir: 1 / (+0) pozitif sonsuzdur; 1 / (-0) negatif sonsuzdur. Sıfır değerler için mantis ve üs bitleri sıfırdır. Not: sıfır değerleri (+0 ve -0) açıkça denormal olarak sınıflandırılmamıştır2.

2 - Bu durum böyle değil denormal sayılarsıfır olan bir ofset üssü (ve ima edilmiş) 0.). Denormal çift hassas sayılar aralığı dmin ≤ | x | ≤ dmaksimum, D neredemin (En küçük temsil edilen sıfır olmayan sayı) 2-1023 - 51 (≈ 4.94 * 10-324) ve dmaksimum (mantissa tamamen oluşan en büyük denormal sayı 1s) 2-1023 + 1 - 2-1023 - 51 (≈ 2.225 * 10-308).


İkili bir kesin sayıyı ikiliye çevirme

Çifte kesinlikli kayan noktalı sayıyı binary'ye çevirmek için birçok çevrimiçi çevirici vardır. binaryconvert.com), ama burada bir çift C # kodu için IEEE 754 gösterimi elde etmek için bir çift duyarlıklı sayı (ben üç parçayı iki nokta ile ayırdım (:):

public static string BinaryRepresentation(double value)
{
    long valueInLongType = BitConverter.DoubleToInt64Bits(value);
    string bits = Convert.ToString(valueInLongType, 2);
    string leadingZeros = new string('0', 64 - bits.Length);
    string binaryRepresentation = leadingZeros + bits;

    string sign = binaryRepresentation[0].ToString();
    string exponent = binaryRepresentation.Substring(1, 11);
    string mantissa = binaryRepresentation.Substring(12);

    return string.Format("{0}:{1}:{2}", sign, exponent, mantissa);
}

Konuya ulaşmak: asıl soru

(TL; DR versiyonu için aşağıya atla)

Cato Johnston (soru sorumlusu) neden 0,1 + 0,2 = 0,3 olduğunu sordu.

İkili olarak yazılmış (üç parçayı ayıran sütunlarla), IEEE 754 değerlerinin temsilleri:

0.1 => 0:01111111011:1001100110011001100110011001100110011001100110011010
0.2 => 0:01111111100:1001100110011001100110011001100110011001100110011010

Mantissa'nın yinelenen rakamlardan oluştuğunu unutmayın. 0011. Bu anahtar hesaplamalarda neden bir hata olduğu - 0.1, 0.2 ve 0.3 ikili olarak gösterilemez tam içinde sınırlı 1/9, 1/3 veya 1/7'den daha fazla ikili bit sayısı tam olarak gösterilebilir Ondalık basamak.

Üslerin ondalık sayıya dönüştürülmesi, ofsetin kaldırılması ve ima edilenin yeniden eklenmesi 1 (köşeli parantez içinde), 0,1 ve 0,2:

0.1 = 2^-4 * [1].1001100110011001100110011001100110011001100110011010
0.2 = 2^-3 * [1].1001100110011001100110011001100110011001100110011010

İki sayı eklemek için, üssün aynı olması gerekir, örn .:

0.1 = 2^-3 *  0.1100110011001100110011001100110011001100110011001101(0)
0.2 = 2^-3 *  1.1001100110011001100110011001100110011001100110011010
sum = 2^-3 * 10.0110011001100110011001100110011001100110011001100111

Toplamı form 2'den olmadığındann * 1. {bbb} üssü bir artırdık ve ondalık kaydıracağızikili) almak için nokta:

sum = 2^-2 * 1.0011001100110011001100110011001100110011001100110011(1)

Mantisde artık 53 bit var (53, yukarıdaki satırdaki köşeli parantez içinde). Varsayılan yuvarlama modu IEEE 754 için 'En Yakın Tur'- eğer bir sayı varsa x iki değer arasında düşer bir ve bEn az anlamlı bitin sıfır olduğu değer seçilir.

a = 2^-2 * 1.0011001100110011001100110011001100110011001100110011
x = 2^-2 * 1.0011001100110011001100110011001100110011001100110011(1)
b = 2^-2 * 1.0011001100110011001100110011001100110011001100110100

Bunu not et bir ve b sadece son bittiğinde farklılık gösterir; ...0011 + 1 = ...0100. Bu durumda, sıfırın en az anlamlı biti olan değer bYani toplamı:

sum = 2^-2 * 1.0011001100110011001100110011001100110011001100110100

TL; DR

yazı 0.1 + 0.2 IEEE 754 ikili temsilinde (üç parçayı ayıran kolonlarla) ve 0.3, bu (farklı bitleri köşeli parantez içine koyuyorum):

0.1 + 0.2 => 0:01111111101:0011001100110011001100110011001100110011001100110[100]
0.3       => 0:01111111101:0011001100110011001100110011001100110011001100110[011]

Ondalık olarak geri dönüştürülür, bu değerler şunlardır:

0.1 + 0.2 => 0.300000000000000044408920985006...
0.3       => 0.299999999999999988897769753748...

Fark tam 2-54, ~ 5.5511151231258 × 10 olan-17 - Orijinal değerlerle karşılaştırıldığında önemsiz (birçok uygulama için).

Kayan nokta sayısının son birkaç bitini karşılaştırmak, ünlüleri okuyan herkes için tehlikelidir.Her Bilgisayar Bilimcinin Kayan Nokta Aritmetiği Hakkında Bilmesi Gerekenler"(bu cevabın tüm önemli kısımlarını kapsayan) bilecek.

Çoğu hesap makinesi ek kullanır bekçi hanesi Bu sorunu çözmek için nasıl 0.1 + 0.2 verirdi 0.3: Son birkaç bit yuvarlanır.


81
2018-03-16 05:27



Cevabım yayınlandıktan kısa bir süre sonra reddedildi. O zamandan beri birçok değişiklik yaptım (orijinalde ihmal ettiğim ikilide 0.1 ve 0.2 yazarken yinelenen bitleri açıkça belirtmek de dahil). Aşağı seçmenin bunu görmesi ihtimaline karşı, cevabımı geliştirebilmem için bana biraz geri bildirim verebilir misiniz? Cevaplarımın yeni bir şey eklediğini hissediyorum çünkü IEEE 754'teki toplamın tedavisi diğer cevaplarda aynı şekilde ele alınmıyor. "Her bilgisayar bilimcisi ne bilmeli ..." aynı malzemeyi kapsar, cevaplarım özellikle 0,1 + 0,2 ile. - Wai Ha Lee


Bilgisayarda saklanan kayan noktalı sayılar iki bölümden, bir tam sayıdan ve tabanın tamsayı kısmı tarafından alındığı ve çarpıldığı bir üsden oluşur.

Bilgisayar 10 üssünde çalışıyorsa, 0.1 olabilir 1 x 10⁻¹, 0.2 olabilir 2 x 10⁻¹, ve 0.3 olabilir 3 x 10⁻¹. Tamsayı matematik, kolay ve kesin, bu yüzden ekleme 0.1 + 0.2 belli ki sonuçlanacak 0.3.

Bilgisayarlar genellikle temel 10'da çalışmazlar, 2 numaralı tabanda çalışırlar. Örneğin, bazı değerler için kesin sonuçlar alabilirsiniz. 0.5 olduğu 1 x 2⁻¹ ve 0.25 olduğu 1 x 2⁻²ve bunları ekleyerek sonuçları 3 x 2⁻²veya 0.75. Kesinlikle.

Sorun, temel 10'da tam olarak gösterilebilen sayılar ile birlikte gelir, ancak temel 2'de değil. Bu sayıların en yakın eşdeğerlerine yuvarlanması gerekir. Çok yaygın IEEE 64-bit kayan nokta biçimi varsayarsak, en yakın 0.1 olduğu 3602879701896397 x 2⁻⁵⁵ve en yakın 0.2 olduğu 7205759403792794 x 2⁻⁵⁵; bunları bir araya getirmek 10808639105689191 x 2⁻⁵⁵veya tam bir ondalık değeri 0.3000000000000000444089209850062616169452667236328125. Kayan nokta sayıları genellikle ekran için yuvarlaktır.


48
2018-02-25 21:42



@Mark Bu açık açıklama için teşekkür ederiz, ancak soru 0,1 + 0,4'ün tam olarak 0,5'e (Python 3'te en az) neden eklendiğini sormaktadır. Ayrıca Python 3'te yüzenleri kullanırken eşitliği kontrol etmenin en iyi yolu nedir? - pchegoor
@ user2417881 IEEE kayan nokta işlemleri, her işlem için yuvarlama kurallarına sahiptir ve bazen yuvarlama, iki sayı biraz kapalı olduğunda bile kesin bir yanıt verebilir. Ayrıntılar bir yorum için çok uzun ve ben de zaten bir uzman değilim. Bu cevapta gördüğünüz gibi 0.5, ikili olarak temsil edilebilecek birkaç ondalıktan biridir, ancak bu sadece bir tesadüf. Eşitlik testi için bkz. stackoverflow.com/questions/5595425/.... - Mark Ransom
@ user2417881 sorusu beni şaşırttı, ben de tam bir soruya cevap verdim ve cevap verdim: stackoverflow.com/q/48374522/5987 - Mark Ransom


Kayan nokta yuvarlama hatası. itibaren Her Bilgisayar Bilimcinin Kayan Nokta Aritmetiği Hakkında Bilmesi Gerekenler:

Sonsuz sayıda gerçek sayıyı sonlu sayıda bitle sıkıştırmak yaklaşık bir gösterim gerektirir. Sonsuz sayıda çok sayıda sayı olmasına rağmen, çoğu programda tamsayı hesaplamaları sonucu 32 bit olarak saklanabilir. Tersine, herhangi bir sabit sayıda bit verildiğinde, gerçek sayılardaki çoğu hesaplama, bu çok bit kullanılarak tam olarak temsil edilemeyen miktarlar üretecektir. Bu nedenle, bir kayan nokta hesaplamasının sonucu, sonlu temsiline geri dönmek için sık sık yuvarlanmalıdır. Bu yuvarlama hatası, kayan nokta hesaplamanın karakteristik özelliğidir.


40
2017-12-26 06:51





Benim geçici çözümüm:

function add(a, b, precision) {
    var x = Math.pow(10, precision || 2);
    return (Math.round(a * x) + Math.round(b * x)) / x;
}

hassas Ekleme sırasında ondalık noktadan sonra korumak istediğiniz basamak sayısını ifade eder.


29
2017-10-05 18:39