Czy istnieje sposób, aby programowo uzyskać podwójną wartość najbliższą 1,0, ale w rzeczywistości nie jest to 1,0?
Jednym z hackerskich sposobów byłoby zapamiętanie podwójnej liczby całkowitej do tej samej wielkości, a następnie odjęcie jednej. Sposób działania formatów zmiennoprzecinkowych IEEE754 skutkowałby zmniejszeniem wykładnika o jeden podczas zmiany części ułamkowej ze wszystkich zer (1,000000000000) na jedynki (1,111111111111). Istnieją jednak maszyny, w których liczby całkowite są przechowywane w postaci little-endian, podczas gdy zmiennoprzecinkowe są przechowywane w postaci big-endian, więc to nie zawsze będzie działać.
c++
floating-point
floating-accuracy
jorgbrown
źródło
źródło
nextafter()
to jedyny właściwy sposób osiągnięcia tego, czego chce.1.0000...
binarna jest dekrementowana do0.111111....
i aby ją znormalizować, musisz przesunąć ją w lewo:1.11111...
co wymaga odliczenia wykładnika. A potem jesteś 2 ulp od 1.0. Więc nie, odjęcie jednego od wartości całkowitej NIE daje tego, o co tutaj pytamy.Odpowiedzi:
W językach C i C ++ poniższa wartość daje wartość najbliższą 1,0:
#include <limits.h> double closest_to_1 = 1.0 - DBL_EPSILON/FLT_RADIX;
Należy jednak pamiętać, że w nowszych wersjach C ++
limits.h
jest przestarzałe na rzeczclimits
. Ale jeśli i tak używasz kodu specyficznego dla C ++, możesz użyć#include <limits> typedef std::numeric_limits<double> lim_dbl; double closest_to_1 = 1.0 - lim_dbl::epsilon()/lim_dbl::radix;
A jak pisze Jarod42 w swojej odpowiedzi, od C99 lub C ++ 11 możesz również użyć
nextafter
:#include <math.h> double closest_to_1 = nextafter(1.0, 0.0);
Oczywiście w C ++ możesz (a dla późniejszych wersji C ++ powinieneś) dołączyć
cmath
i używaćstd::nextafter
zamiast tego.źródło
Od C ++ 11 możesz użyć,
nextafter
aby uzyskać następną reprezentowalną wartość w podanym kierunku:std::nextafter(1., 0.); // 0.99999999999999989 std::nextafter(1., 2.); // 1.0000000000000002
Próbny
źródło
std::ceil(std::nextafter(1., std::numeric_limits<double>::max()))
.nextafter
, a to jak MUSL Realizuje w przypadku ktokolwiek inny chce zobaczyć jak to się robi. Zasadniczo: kręcenie surowego bitu.W C możesz użyć tego:
#include <float.h> ... double value = 1.0+DBL_EPSILON;
DBL_EPSILON
jest różnicą między 1 a najmniejszą wartością większą niż 1, którą można przedstawić.Aby zobaczyć rzeczywistą wartość, musisz wydrukować go do kilku cyfr.
Na mojej platformie
printf("%.16lf",1.0+DBL_EPSILON)
daje1.0000000000000002
.źródło
1.
jak1'000'000
Demo1.0
. BTW, daje również najbliższą wartość większą niż 1, a nie bezwzględną wartość najbliższą 1 (która jest prawdopodobnie mniejsza niż 1). Zgadzam się więc, że jest to częściowa odpowiedź, ale pomyślałem, że mimo wszystko może się do tego przyczynić.W C ++ możesz również tego użyć
1 + std::numeric_limits<double>::epsilon()
źródło