дефинирайте двойната константа като шестнадесетична?

Бих искал да имам най-близкото число под 1,0 като плаваща запетая. Като прочетох статията в wikipedia за IEEE-754 успях да разбера че двоичното представяне за 1.0 е 3FF0000000000000, така че най-близката двойна стойност всъщност е 0x3FEFFFFFFFFFFFFF.

Единственият начин, който знам за инициализиране на двойно с тези двоични данни, е следният:

double a;
*((unsigned*)(&a) + 1) = 0x3FEFFFFF;
*((unsigned*)(&a) + 0) = 0xFFFFFFFF;

Което е доста тромаво за използване.

Има ли по-добър начин да се дефинира това двойно число, ако е възможно като константа?


person martinus    schedule 16.10.2010    source източник
comment
единственият начин е този... освен ако вашата реализация на C++ няма поддръжка за 64-битово цяло число.   -  person mmx    schedule 16.10.2010
comment
Това е просто заяждане, но е IEEE-754-1985 (не IEEE-745).   -  person George    schedule 16.10.2010


Отговори (6)


Съществуват шестнадесетични float и двойни литерали. Синтаксисът е 0x1.(мантиса)p(експонента в десетична дроб) Във вашия случай синтаксисът ще бъде

double x = 0x1.fffffffffffffp-1
person Shum    schedule 16.10.2010
comment
Никога преди не съм чувал за този синтаксис. Имате ли справка? - person Mark Ransom; 16.10.2010
comment
Мисля, че е част от стандарта C99. Работи с компилатори на GNU, за други не ме интересува. - person Shum; 16.10.2010
comment
@Mark Ransom: Наскоро написах статия за това: exploringbinary.com/hexadecimal-floating -точкови константи - person Rick Regan; 16.10.2010
comment
@Mark Ransom: Добавено в C99. Поддържа се и в printf/scanf чрез спецификатора на формат %a. Досега най-добрият начин за указване на стойности с плаваща запетая в C. - person Stephen Canon; 18.10.2010

Не е безопасно, но нещо като:

double a;
*(reinterpret_cast<uint64_t *>(&a)) = 0x3FEFFFFFFFFFFFFFL;

Това обаче разчита на конкретен ред на числата с плаваща запетая във вашата система, така че не правете това!

Вместо това просто поставете DBL_EPSILON в <cfloat> (или както е посочено в друг отговор, std::numeric_limits<double>::epsilon()) за добра употреба.

person Oliver Charlesworth    schedule 16.10.2010
comment
Третирането му като цяло число трябва да го направи независимо от крайния ред (освен ако нямате една от онези странни смесени системи с крайния ред) - person Rick Regan; 16.10.2010
comment
@Rick Regan: Кой може да каже, че endianness на представянето на вашата платформа на типове с плаваща запетая е в съответствие с представянето на цели числа? - person Oliver Charlesworth; 16.10.2010
comment
Теоретично сте прав -- но имате ли пример (освен смесените крайни „меки плаващи“)? - person Rick Regan; 17.10.2010

Ако направите bit_cast и използвате целочислени типове с фиксирана ширина , може да се направи безопасно:

template <typename R, typename T>
R bit_cast(const T& pValue)
{
    // static assert R and T are POD types

    // reinterpret_cast is implementation defined,
    // but likely does what you expect
    return reinterpret_cast<const R&>(pValue);
}

const uint64_t target = 0x3FEFFFFFFFFFFFFFL;
double result = bit_cast<double>(target);

Въпреки че вероятно можете просто да извадите epsilon от него.

person GManNickG    schedule 16.10.2010
comment
Не сте сигурни защо си направихте труда да дефинирате bit_cast, когато можехте просто да използвате reinterpret_cast<double&> директно. Все още изглежда като добро решение. - person Mark Ransom; 16.10.2010
comment
@Mark: Това няма да твърди статично, че и двата типа са типове POD и би улеснило нарушаването на правилата за псевдоним. (Разбира се, дадох по-общо решение от необходимото; в този случай простото правене директно работи добре.) - person GManNickG; 16.10.2010

Малко е архаично, но можете да използвате union. Ако приемем, че long long и double са с дължина 8 байта във вашата система:

typedef union { long long a; double b } my_union;

int main()
{
    my_union c;
    c.b = 1.0;
    c.a--;
    std::cout << "Double value is " << c.b << std::endl;
    std::cout << "Long long value is " << c.a << std::endl;
}

Тук не е необходимо да знаете предварително какво е битовото представяне на 1.0.

person socket puppet    schedule 16.10.2010
comment
Това води до UB, строго погледнато. - person GManNickG; 16.10.2010

Този 0x1.fffffffffffffp-1 синтаксис е страхотен, но само в C99 или C++17.

Но има заобиколно решение, без кастинг (показател), без UB/IB, просто проста математика.

double x = (double)0x1fffffffffffff / (1LL << 53);

Ако имам нужда от Pi и Pi(double) е 0x1.921fb54442d18p1 в шестнадесетичен, просто напишете

const double PI = (double)0x1921fb54442d18 / (1LL << 51);

Ако вашата константа има голям или малък показател, можете да използвате функцията exp2 вместо shift, но exp2 е C99/C++11 ... Използвайте pow за спасяване!

person kevinjwz    schedule 09.05.2018

Вместо цялото жонглиране с битове, най-директното решение е да използвате nextafter() от math.h. По този начин:

#include <math.h>
double a = nextafter(1.0, 0.0); 

Прочетете това като: следващата стойност с плаваща запетая след 1.0 в посока 0.0; почти директно кодиране на „най-близкото число под 1.0“ от оригиналния въпрос.

person CvR    schedule 09.12.2019