1.0'a en yakın çift nedir, bu 1.0 değil mi?


89

1.0'a en yakın olan, ancak aslında 1.0 olmayan iki katını programlı olarak elde etmenin bir yolu var mı?

Bunu yapmanın bir hileli yolu, iki katı aynı büyüklükte bir tamsayıya hatırlamak ve sonra bir çıkarmaktır. IEEE754 kayan nokta biçimlerinin çalışma şekli, bu, kesirli bölümü tüm sıfırlardan (1.000000000000) hepsine (1.111111111111) değiştirirken üssün bir azalmasıyla sonuçlanır. Bununla birlikte, tam sayıların küçük-endian, kayan nokta ise büyük-endian olarak depolandığı makineler vardır, bu yüzden bu her zaman işe yaramaz.


4
+ 1'in -1 ile aynı mesafede (1,0'dan) olduğunu varsayamazsınız. 10 nolu taban ve 2 nolu taban kayan nokta temsillerinin araya girmesi, boşlukların eşit olmadığı anlamına gelir.
Richard Critten

2
@Richard: haklısın. Bir ULP'nin çıkarılmasının "sonraki-önceki" değerini alması çok düşük bir ihtimaldir, çünkü sanırım üs de ayarlanmalıdır. nextafter()istediğini elde etmenin tek doğru yolu.
Rudy Velthuis

1
Bilginize Bu blogu okudum (benim değil): exploringbinary.com/…
Richard Critten

1
@RudyVelthuis: Her IEEE754 ikili kayan noktalı formatta çalışır.
Edgar Bonet

1
Tamam, o zaman bana söyle: "her IEEE754 kayan nokta biçiminde" ne işe yarar? Eğer önemi azaltırsanız, "firstbefore ()" değerini alırsınız, özellikle de iki kuvveti olan bir anlamı olan 1.0 için değil. Bu, 1.0000...ikilinin azalması anlamına gelir 0.111111....ve onu normalleştirmek için onu sola kaydırmanız gerekir: 1.11111...bu da üssü azaltmanızı gerektirir. Ve sonra 1.0'dan 2 ulp uzaktasınız. Yani hayır, integral değerden bir çıkarmak size burada sorulanları VERMEZ.
Rudy Velthuis

Yanıtlar:


23

C ve C ++ 'da, aşağıdakiler 1.0'a en yakın değeri verir:

#include <limits.h>

double closest_to_1 = 1.0 - DBL_EPSILON/FLT_RADIX;

C sonraki sürümlerinde ++, Ancak unutmayın limits.hlehine kullanımdan kaldırılmıştır climits. Ancak yine de C ++ 'ya özel bir kod kullanıyorsanız,

#include <limits>

typedef std::numeric_limits<double> lim_dbl;
double closest_to_1 = 1.0 - lim_dbl::epsilon()/lim_dbl::radix;

Jarod42'nin cevabında yazdığı gibi, C99 veya C ++ 11'den beri şunları da kullanabilirsiniz nextafter:

#include <math.h>

double closest_to_1 = nextafter(1.0, 0.0);

Elbette C ++ 'da bunun yerine dahil edebilir cmathve kullanabilirsiniz (ve daha sonraki C ++ sürümleri için gerekir) std::nextafter.


144

C ++ 11'den beri nextafter, verilen yönde bir sonraki gösterilebilir değeri elde etmek için kullanabilirsiniz :

std::nextafter(1., 0.); // 0.99999999999999989
std::nextafter(1., 2.); // 1.0000000000000002

Demo


11
Bu aynı zamanda bir sonraki gösterilebilen tamsayıya çift artırmak için güzel bir yoludur: std::ceil(std::nextafter(1., std::numeric_limits<double>::max())).
Johannes Schaub -

44
Bir sonraki soru "bu stdlib'de nasıl uygulanacak" olacak: P
Yörüngede Hafiflik Yarışları

18
@ LightnessRacesinOrbit'in yorumunu okuduktan sonra merak ettim. Bu nasıl glibc uygular olduğunextafter , ve bu musl uygular bunu nasıl başka durumda herkes nasıl yapıldığını görmek istiyor. Temel olarak: ham biraz oynatma.
Cornstalks

2
@Cornstalks: Biraz oynamasına şaşırmadım, diğer tek seçenek CPU desteğine sahip olmak olabilir.
Matthieu M.

5
Bunu düzgün yapmanın tek yolu biraz oynatmak, IMO. Yavaş yavaş yaklaşmaya çalışarak birçok test denemesi yapabilirsiniz, ancak bu çok yavaş olabilir.
Rudy Velthuis

23

C'de bunu kullanabilirsiniz:

#include <float.h>
...
double value = 1.0+DBL_EPSILON;

DBL_EPSILON 1 ile 1'den büyük olan en küçük değer arasındaki farktır.

Gerçek değeri görmek için birkaç basamak yazdırmanız gerekir.

Benim platformumda printf("%.16lf",1.0+DBL_EPSILON)verir 1.0000000000000002.


10
Yani daha başka değerler için bu alışkanlık iş 1.olarak 1'000'000 Demo
Jarod42

7
@ Jarod42: Haklısın, ama OP özellikle soruyor 1.0. BTW, aynı zamanda 1'den büyük en yakın değeri verir ve 1'e mutlak en yakın değeri vermez (muhtemelen 1'den küçüktür). Bu yüzden bunun kısmi bir cevap olduğuna katılıyorum, ancak yine de katkıda bulunabileceğini düşündüm.
barak manos

@ LưuVĩnhPhúc: Cevabın kısıtlanması ve diğer yöne en yakın olanı konusunda kesinlik veriyorum.
Jarod42

7
Bu, 1.0'a en yakın duble vermez , çünkü (2 tabanını varsayarsak) 1.0'dan hemen önceki duble , 1.0'dan sonraki dublörün (hesapladığınız) sadece yarısı kadardır .
celtschk

@celtschk: Haklısın, bunu yukarıdaki yorumda açıkladım.
barak manos

4

C ++ 'da bunu da kullanabilirsiniz

1 + std::numeric_limits<double>::epsilon()


2
@zwol teknik olarak tipik ikili kayan nokta uygulamaları için 1 ve 2-epsilon arasındaki herhangi bir değer için çalışacaktır. Ancak, evet, sadece 1'e başvurmanın garantili olduğu konusunda haklısınız.
Random832

7
Teknik olarak, 1 için çalışmaz, çünkü 1'e en yakın sayı 1'den hemen önceki sayıdır, hemen arkasındaki sayı değildir. Çift 0.5 ve 1 arasında s' hassas kadar yakın 1'e 1 bitmeden dolayısıyla sayı doğru, 1 ve 2 arasındaki hassas olarak en yüksek iki kat
Hellogoodbye
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.