C как получить точное двойное значение (точность) из целого числа без знака

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

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

Окончательное значение для вычисленной контрольной суммы представляет собой целое число без знака, но перед этим оно преобразуется из числа double в значение unsigned long long, также известное как unsigned __int64.

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

Пока вычисляется контрольная сумма, это значение, которое она генерирует в типе данных double. 3083570000.3115764 который создает контрольную сумму 0xB7CB8B50

Я не думаю, что это разговор с потерями, поэтому на самом деле ничего не потеряно, даже если он преобразует 8-байтовую двойную контрольную сумму обратно в 4-байтовую целочисленную контрольную сумму. Почему? потому что значение double всегда создается путем умножения на 4294967295.0, что, как я полагаю, предназначено только для устранения последних 4 байтов, вроде сдвига.

Таким образом, значение double, которое я хочу получить, должно быть разделено на 4294967295.0, чтобы точно вернуть исходное двойное значение до последней цифры.

Проблема в том, что я не могу разделить его правильно, потому что он не на 100% точен до последней десятичной точки. просто пытаюсь изменить это так же, как оно было создано в первую очередь.

Выходные данные Предположим, что исходная двойная контрольная сумма была 0.71794958809146792
0.71794958809146792 * 4294967295.0 = 3083570000.3115761849416764
ответ, отправленный в пакете, равен 0xb7cb8b50

если я вручную преобразую целое число без знака 0xb7cb8b50 в unsigned __int64, оно должно выглядеть так 0x00000000b7cb8b50

Первоначальный дубликат того, как он был сгенерирован в коде, должен выглядеть так, я использовал тот же ключ до того, как он был добавлен к пакету, чтобы воссоздать те же условия, чтобы сделать контрольную сумму в первую очередь, и это должно выглядеть так
Real ANSWER = 3083570000.3115764

So
0x00000000b7cb8b50 should equals = 3083570000.3115764 double

Мой обратный код выглядит так

unsigned int checksum = 0xb7cb8b50;
double test1 = (double)(unsigned __int64)checksum;
double test2 = double(checksum);
double test3 = static_cast<double>(checksum);
double test4 = *((double*)(void*)&checksum);

приведенный выше код неверен на несколько знаков после запятой.

test1 returns = 3083570000.0000000
test2 returns = 3083570000.0000000
test3 returns = 3083570000.0000000
test4 returns = 1.523486003547e-314#DEN

Как мне получить доп, .3115764 тоже мой вопрос.


person SSpoke    schedule 26.11.2015    source источник
comment
Выберите один язык...   -  person Lightness Races in Orbit    schedule 27.11.2015
comment
Приведение double к int удаляет все после запятой.   -  person user253751    schedule 27.11.2015
comment
@LightnessRacesinOrbit извините за то, что я предпочитаю получать ответ на любом языке. С или С++   -  person SSpoke    schedule 27.11.2015
comment
@immibis, значит, вы говорите, что невозможно вернуть данные с десятичной точкой? это на самом деле неплохая информация, которую нужно знать, это означает, что контрольная сумма не может быть изменена правильно? Таким образом, десятичные данные усекаются?   -  person SSpoke    schedule 27.11.2015
comment
Есть ли целое число без знака в двойной преобразователь, который немного более интеллектуальная функция, которая увеличивает двойное значение на небольшое десятичное число, может быть, процент% для большей точности, чтобы вы могли настроить его для каждого беззнакового значения, которое вы бросаете в него, чтобы, возможно, получить оставшееся десятичное число точки?   -  person SSpoke    schedule 27.11.2015
comment
@SSpoke: Наверняка вы работаете в одном из них, так что именно в нем вам нужен ответ.   -  person Lightness Races in Orbit    schedule 27.11.2015
comment
@LightnessRacesinOrbit Конечно, он достаточно умен, чтобы преобразовать ответ C в C++ или наоборот.   -  person user253751    schedule 27.11.2015
comment
@immibis: Если бы он/она знал достаточно об этой теме, чтобы сделать это правильно, точно, безопасно и идиоматически, тогда ему/ей не пришлось бы задавать вопрос.   -  person Lightness Races in Orbit    schedule 27.11.2015
comment
@LightnessRacesinOrbit Почему бы и нет? Задаваемый вопрос совершенно не связан с преобразованием ответов C и C++.   -  person user253751    schedule 27.11.2015
comment
переход от значения int к значению double не всегда будет точным совпадением   -  person user3629249    schedule 29.11.2015
comment
Возможно, я упускаю какую-то ключевую деталь, но я ничего не вижу в вопросе о преобразовании между C и C++.   -  person user3629249    schedule 29.11.2015
comment
в общем случае контрольная сумма является односторонней операцией. Его можно вычислить повторно из исходных исходных данных, но исходные данные не могут быть перегенерированы из контрольной суммы.   -  person user3629249    schedule 29.11.2015
comment
@user3629249 user3629249 да, я снял тег и заголовок C ++, но, как я уже сказал, я бы не возражал против кода, кажется, здесь каждый вопрос создан только для 1 языка. Да, это хеш контрольной суммы, но части контрольной суммы, около половины, преобразуются с помощью множества побитовых операций, а затем преобразуются в двойные, за которыми следует двойное умножение максимального целого числа без знака в двойном формате (4294967295.0), где половина данных теряется при обратном преобразовании в беззнаковое целое, где наконец возвращается хэш контрольной суммы. Да, это контрольная сумма, но мне кажется, что она не имела бы смысла, если бы не была обратимой.   -  person SSpoke    schedule 29.11.2015
comment
@user3629249 user3629249 У меня просто возникло внутреннее ощущение, что это число 4294967295.0 как-то связано с данными, которые в конечном итоге будут потеряны при преобразовании в беззнаковое целое, что-то вроде сжатия, поэтому любая потеря не будет иметь значения, я как бы отказался от этого реверсирования контрольной суммы, но , у меня была теория о том, что доли десятичных знаков могут быть сгенерированы с помощью статической процентной переменной, где каждая десятичная точка увеличивается на определенный процент для каждого целого числа до десятичной точки.   -  person SSpoke    schedule 29.11.2015


Ответы (1)


Дробная часть (после запятой) числа double теряется, когда оно присваивается целому числу.

Выполняя обратную операцию (присваивая полученное int double), дробная часть будет равна 0. Таким образом, невозможно восстановить исходное значение числа double.

В выводе следующего кода видно, как значение double хранится в памяти (старшие 32 бита не равны 0):

double initial = 0.71794958809146792 * 4294967295.0;
uint64_t rawValue = *(uint64_t*)&initial;
uint32_t checksum = (uint32_t)initial;
printf("initial: %f 0x%llx\n", initial, rawValue);
printf("after:   %u 0x%x\n", checksum, checksum);
// Prints
// initial: 3083570000.311576 0x41e6f9716a09f86f
// after:   3083570000 0xb7cb8b50

Попытка привести необработанное значение int к double (как в *((double*)(void*)&checksum)) является некорректной операцией, и она может даже получить доступ к памяти, не принадлежащей checksum (если int размер, если 4 байта).

Дополнительную информацию о представительстве double можно найти по адресу:

https://en.wikipedia.org/wiki/Double-precision_format_floating-point

person nnn    schedule 26.11.2015
comment
Значит, потерянные данные вообще нельзя предсказать? - person SSpoke; 27.11.2015
comment
@SSpoke Нет, это никогда нельзя было бы предсказать / восстановить. - person nnn; 27.11.2015
comment
Спасибо, я все еще верю, что это обратимо, разница точная 0.71794958809146797007216791856852 против неточной 0.71794958801892343629592178303188, и я считаю, что первые 8 или 9 десятичных знаков могут содержать решение, чтобы полностью отменить весь процесс в любом случае, поскольку это значение 0,7 создается 4 беззнаковыми шортами и 8 байтами. .. что-то подсказывает мне, что эти 8 или 9 десятичных знаков, которые точны в обоих случаях, все еще могут содержать большую часть решения, поскольку переднее значение этого 0.7 всегда равно 1.0, контрольная сумма включает в себя выполнение result = checksum - 1.0; Спасибо за понимание, я отмечу это как отвечать. - person SSpoke; 27.11.2015
comment
uint64_t rawValue = *(uint64_t*)&initial; вызывает неопределенное поведение, нарушая строгое правило псевдонимов. - person M.M; 27.11.2015
comment
@MM Верно, спасибо. Я думаю, это можно исправить, используя union для записи initial, а затем для чтения rawValue. Или используя memcpy(&rawValue, &initial, 8); - person nnn; 27.11.2015