Все ли целые значения прекрасно представлены как двойные?
Мой вопрос заключается в том, гарантировано ли у всех целочисленных значений идеальное двойное представление.
Рассмотрим следующий пример кода, который печатает "Тот же":
// 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
?
Я попробовал это для некоторых других чисел, и это всегда было правдой, но не смогло найти ничего о том, совпадение или дизайн.
Примечание. Это отличается от этого вопроса (кроме языка), так как я добавляю два целых числа.
Ответы
Ответ 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
.