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


этот вопрос уже есть ответ здесь:

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

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

// 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?

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

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

5   51   2017-04-27 13:51:28

5 ответов:

отказ от ответственности (как предложил Тоби Спейт): хотя представления 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

Schematic of IEEE 754 double type

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

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

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

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

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

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


для эмпирически склонных, попробуйте этой:

#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


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

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

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

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

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

например, этой

#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 есть.

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

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

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

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

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

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

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

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

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

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