Подтвердить что ты не робот

Все ли целые значения прекрасно представлены как двойные?

Мой вопрос заключается в том, гарантировано ли у всех целочисленных значений идеальное двойное представление.

Рассмотрим следующий пример кода, который печатает "Тот же":

// Example program
#include <iostream>
#include <string>

int main()
{
  int a = 3;
  int b = 4;
  double d_a(a);
  double d_b(b);

  double int_sum = a + b;
  double d_sum = d_a + d_b;

  if (double(int_sum) == d_sum)
  {
      std::cout << "Same" << std::endl;
  }
}

Гарантируется ли это для любой архитектуры, любого компилятора, любых значений a и b? Будет ли любое целое число i преобразовано в double, всегда будет представлено как i.0000000000000, а не, например, как i.000000000001?

Я попробовал это для некоторых других чисел, и это всегда было правдой, но не смогло найти ничего о том, совпадение или дизайн.

Примечание. Это отличается от этого вопроса (кроме языка), так как я добавляю два целых числа.

4b9b3361

Ответ 1

Отказ от ответственности (как было предложено Тоби Спейтом): Хотя представления IEEE 754 довольно распространены, реализации разрешено использовать любое другое представление, которое удовлетворяет требованиям языка.


Дублины представлены в форме mantissa * 2^exponent, то есть некоторые из битов используются для нецелочисленной части двойного числа.

             bits        range                       precision
  float        32        1.5E-45   .. 3.4E38          7- 8 digits
  double       64        5.0E-324  .. 1.7E308        15-16 digits
  long double  80        1.9E-4951 .. 1.1E4932       19-20 digits

Схема двойного типа IEEE 754

Часть во фракции также может использоваться для представления целого числа с использованием показателя степени, который удаляет все цифры после точки.

например. 2,9979 · 10 ^ 4 = 29979.

Поскольку обычный int обычно 32 бит, вы можете представить все int как double, но для 64-битных целых чисел, конечно, это уже не так. Чтобы быть более точным (как указано в комментарии LThode): двойная точность IEEE 754 может гарантировать это до 53 бит (52 бит значения + неявный ведущий 1 бит).

Ответ: да для 32-битных int, нет для 64-битных ints.

(Это правильно для сред общего назначения для серверов и настольных компьютеров, но другие архитектуры могут вести себя по-другому.)

Практический ответ, как говорит Malcom McLean: 64-разрядные двойники являются адекватным целым типом для почти всех целых чисел, которые, вероятно, будут считать вещи в реальной жизни.


Для эмпирически наклонного, попробуйте this:

#include <iostream>
#include <limits>
using namespace std;

int main() {
    double test;
    volatile int test_int;
    for(int i=0; i< std::numeric_limits<int>::max(); i++) {
        test = i;
        test_int = test;

        // compare int with int:
        if (test_int != i)
            std::cout<<"found integer i="<<i<<", test="<<test<<std::endl;
    }
    return 0;
}

Время успеха: 0,85 память: сигнал 15240: 0


подвопрос: Что касается вопроса о дробных различиях. Возможно ли иметь целое число, которое преобразуется в двойное число, которое не соответствует правильному значению на долю, но которое возвращается к тому же целому числу из-за округления?

Ответ - нет, потому что любое целое число, которое преобразует назад и вперед в одно и то же значение, фактически представляет одно и то же целочисленное значение в double. Для меня простое объяснение (предложенное ilkkachu) для этого состоит в том, что с использованием показателя 2^exponent ширина шага должна всегда быть степенью двух. Следовательно, за пределами наибольшего битового бита 52 (+1 знак) никогда не бывает двух двойных значений с расстоянием меньше 2, что решает проблему округления.

Ответ 2

Нет. Предположим, у вас есть 64-разрядный целочисленный тип и 64-разрядный тип с плавающей запятой (что характерно для double). Для этого целочисленного типа существует 2 ^ 64 возможных значения, и для этого типа с плавающей запятой возможны 2 ^ 64 возможных значения. Но некоторые из этих значений с плавающей запятой (фактически, большинство из них) не представляют собой целочисленные значения, поэтому тип с плавающей запятой может представлять меньше целочисленных значений, чем тип integer.

Ответ 3

Ответ - нет. Это работает только в том случае, если int - 32 бит, что, хотя и верно для большинства платформ, не гарантируется стандартом.

Два целых числа могут использовать одно и то же двойное представление.

Например, this

#include <iostream>
int main() {
    int64_t n = 2397083434877565865;
    if (static_cast<double>(n) == static_cast<double>(n - 1)) {
        std::cout << "n and (n-1) share the same double representation\n";
    }
}    

напечатает

n и (n-1) используют одно и то же двойное представление

т.е. оба 2397083434877565865 и 2397083434877565864 преобразуются в тот же double.

Обратите внимание, что я использовал int64_t здесь, чтобы гарантировать 64-битные целые числа, которые - в зависимости от вашей платформы - также могут быть int.

Ответ 4

У вас есть 2 разных вопроса:

Являются ли все целые значения идеально представленными как двойные?

Об этом уже ответили другие люди (TL; DR: это зависит от точности int и double).

Рассмотрим следующий пример кода, который печатает "Same": [...] Гарантируется ли это, что это правда для любой архитектуры, любого компилятора, любых значений a и b?

Ваш код добавляет два int, а затем преобразует результат в double. Сумма int будет переполняться для определенных значений, но сумма двух отдельно преобразованных double не будет (обычно). Для этих значений результаты будут отличаться.

Ответ 5

Короткий ответ "возможно". Портативный ответ "не везде".

Это действительно зависит от вашей платформы и, в частности, от

  • размер и представление double
  • диапазон int

Для платформ с удвоением IEEE-754 это может быть правдой, если int - 53 бит или меньше. Для платформ, где int больше, чем double, это явно неверно.

Возможно, вам захочется исследовать свойства вашего хоста во время выполнения, используя std::numeric_limits и std::nextafter.