Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > непонятное содержимое массива после десериализации


Автор: boostcoder 27.5.2012, 21:29
привет.

сериализую массив 'long double'ов(исходный массив инициализирован целыми: 1,2,3,4,5,6) в текстовый архив. после десериализации, при выводе полученного массива на консоль, все отображается корректно.
проблема проявляется при сравнивании массивов с помощью memcmp(). при том, проблема проявилась только с текстовой сериализацей.

hex дамп исходного массива:
Цитата

0000000: 00 00 00 00 00 00 00 80 ff 3f 00 00 00 00 00 00   .........?......
0000016: 00 00 00 00 00 00 00 80 00 40 00 00 ff 7f 00 00   .........@......
0000032: 00 00 00 00 00 00 00 c0 00 40 00 00 ff 7f 00 00   .........@......
0000048: 00 00 00 00 00 00 00 80 01 40 00 00 ff 7f 00 00   .........@......
0000064: 00 00 00 00 00 00 00 a0 01 40 00 00 ff 7f 00 00   .........@......
0000080: 00 00 00 00 00 00 00 c0 01 40 00 00 00 00 00 00   .........@......
length: 96 bytes. CRC32: 0x8a940c74

hex дамп полученного массива:
Цитата

0000000: 00 00 00 00 00 00 00 80 ff 3f ff ff 00 00 00 00   .........?......
0000016: 00 00 00 00 00 00 00 80 00 40 00 00 ff 7f 00 00   .........@......
0000032: 00 00 00 00 00 00 00 c0 00 40 00 00 00 00 00 00   .........@......
0000048: 00 00 00 00 00 00 00 80 01 40 00 00 ff 7f 00 00   .........@......
0000064: 00 00 00 00 00 00 00 a0 01 40 64 f7 ff 7f 00 00   .........@d.....
0000080: 00 00 00 00 00 00 00 c0 01 40 ff ff ff 7f 00 00   .........@......
length: 96 bytes. CRC32: 0x4a3c2a42


что-то я даже не могу представить, почему это массивы содержат нужные значения, но при этом имеют разные дампы памяти smile 
подскажите из-за чего, и как лечить?

спасибо.

Автор: Alexeis 27.5.2012, 21:45
long double случаем не 10ти байтовый? Может элементы выравнены на границу 4х байт и собственно отличаются мусором, которым забиваются байты выравнивания?

Автор: boostcoder 27.5.2012, 21:48
Цитата(Alexeis @  27.5.2012,  21:45 Найти цитируемый пост)
long double случаем не 10ти байтовый?

проверяю на x86_64. тут sizeof(long double) == 16.

Цитата(Alexeis @  27.5.2012,  21:45 Найти цитируемый пост)
Может элементы выравнены на границу 4х байт и собственно отличаются мусором, которым забиваются байты выравнивания? 

эм.. может.
а как в этом убедится? как исправить?

Автор: Alexeis 27.5.2012, 22:06
Цитата(boostcoder @  27.5.2012,  22:48 Найти цитируемый пост)
проверяю на x86_64. тут sizeof(long double) == 16.

  Скорее всего long double реально занимает 10 байт, потому что сопроцессор больше не умеет, а вот компилятор для оптимизации по скорости увеличил размер типа добавив лишних 6 байт, которые он не читает и не пишет и в сопроцессор они никак не попадают, поэтому могут содержать в себе что угодно. Проверить можно так. Сначала заполнить память заборчиком из бит 0xAA дает последовательность 01010101 , при помощи memset, а затем записать поверх заборчика полезные данные. После чего глянуть дамп. В каждом из блоков по 16 байт должны проглядываться фрагменты заборчика.

Автор: boostcoder 27.5.2012, 22:14
Цитата(Alexeis @  27.5.2012,  22:06 Найти цитируемый пост)
long double реально занимает 10 байт

оно так на 32ух битной машине.
на 64ех битной  - равняется 16.
вот тому подтверждение: http://www.physics.uq.edu.au/people/foster/amd64_porting.html

Цитата(Alexeis @  27.5.2012,  22:06 Найти цитируемый пост)
Сначала заполнить память заборчиком из бит 0xAA дает последовательность 01010101 , при помощи memset, а затем записать поверх заборчика полезные данные. После чего глянуть дамп. В каждом из блоков по 16 байт должны проглядываться фрагменты заборчика. 

таки вариант smile
сейчас проверю...

Добавлено через 7 минут и 41 секунду
Цитата(boostcoder @  27.5.2012,  22:14 Найти цитируемый пост)
на 64ех битной  - равняется 16.

проверил еще и на венде. тоже 16.

Автор: mes 27.5.2012, 22:53
Цитата(boostcoder @  27.5.2012,  21:14 Найти цитируемый пост)
вот тому подтверждение: 

это не  мешает быть полезными толъко 10 битам из 16..

Автор: boostcoder 28.5.2012, 09:17
хм..
без использования сериализации этот баг я немогу воспроизвести не на LWS, не на локальной машине smile 
код такой:
Код

#include <iostream>
#include <sstream>
#include <string>
#include <iterator>
#include <cstring>

/***************************************************************************/

int main() {
   constexpr size_t array_size = 6;
   long double lda1[array_size], lda2[array_size];

   std::ostringstream os;
   for ( size_t idx = 0; idx < array_size; ++idx ) {
      lda1[idx] = idx;
      os << lda1[idx] << ' ';
   }

   std::istringstream is(os.str());
   for ( size_t idx = 0; idx < array_size; ++idx ) {
      is >> lda2[idx];
   }

   std::cout << "lda1: ";
   std::copy(std::begin(lda1), std::end(lda1), std::ostream_iterator<long double>(std::cout, " "));
   std::cout << std::endl;
   std::cout << "lda2: ";
   std::copy(std::begin(lda2), std::end(lda2), std::ostream_iterator<long double>(std::cout, " "));
   std::cout << std::endl;

   bool ok = memcmp(&lda1[0], &lda2[0], sizeof(lda1[0])&array_size) == 0;
   std::cout << "arrays is " << (ok?"equal":"not equal") << std::endl;
}

/***************************************************************************/


http://liveworkspace.org/code/3f721237a32a90a9afceb2a1d2434115

а вот если std::ostringstream и std::istringstream заменить на типы сериализатора/десериализатора - то ошибка восспроизведется.

Автор: borisbn 28.5.2012, 10:41
Цитата(boostcoder @  28.5.2012,  09:17 Найти цитируемый пост)
bool ok = memcmp(&lda1[0], &lda2[0], sizeof(lda1[0])&array_size) == 0;

если заменить на умножение, то получаем
Цитата
arrays is not equal

http://liveworkspace.org/code/78c1ff19c35133d4ebbd7fb2fd099b7f

Добавлено через 3 минуты и 12 секунд
если long double заменить на long, то получаем equal.
Наверное проблема в переводе плавающих чисел в строку и обратно. например, для числа 1,5 вполне может не оказаться двоичного представления, и ближайшее к нему будет равно 1,4(9), однако, при переводе его в строку, мы получим 1,5

Автор: boostcoder 28.5.2012, 10:54
Цитата(boostcoder @  28.5.2012,  09:17 Найти цитируемый пост)
sizeof(lda1[0])&array_size

жуть какая smile 

Цитата(borisbn @  28.5.2012,  10:41 Найти цитируемый пост)
Наверное проблема в переводе плавающих чисел в строку и обратно. например, для числа 1,5 вполне может не оказаться двоичного представления, и ближайшее к нему будет равно 1,4(9)

я тоже так думаю. вот только не знаю. на сколько такое поведение правильно..

Автор: mes 28.5.2012, 10:54
Цитата(boostcoder @  27.5.2012,  20:29 Найти цитируемый пост)
что-то я даже не могу представить, почему это массивы содержат нужные значения, но при этом имеют разные дампы памяти  

 а чем десериализируете?

P.S. если вопрос о том могут ли равные значения бытьпредставлены разным набором битов, то ответ : могут smile


Автор: boostcoder 28.5.2012, 10:55
исправленный код: http://liveworkspace.org/code/d57ddd3da320a456988591407d374eac

Добавлено через 1 минуту и 13 секунд
Цитата(mes @  28.5.2012,  10:54 Найти цитируемый пост)
 а чем десериализируете?

https://github.com/niXman/yas.

Автор: mes 28.5.2012, 10:56
Цитата(boostcoder @  28.5.2012,  09:54 Найти цитируемый пост)
 вот только не знаю. на сколько такое поведение правильно.. 

для плавающих законно smile

Автор: boostcoder 28.5.2012, 11:02
значит тест на сравнение байтового массива для таких типов некорректен?

Автор: borisbn 28.5.2012, 11:09
хммм. а ведь проблема именно в long double.
c float и с double - всё в порядке.
http://liveworkspace.org/code/d85ee8ad014c23bcd4c3cfac76e72602
значит это
Цитата(borisbn @  28.5.2012,  10:41 Найти цитируемый пост)
Наверное проблема в переводе плавающих чисел в строку и обратно

не совсем верно

Автор: boostcoder 28.5.2012, 11:12
Цитата(borisbn @  28.5.2012,  11:09 Найти цитируемый пост)
проблема именно в long double.

выходит так..
так может вовсе запретить использование 'long double' ? или оставить на страх и риск юзеров?

Автор: mes 28.5.2012, 11:16
Цитата(boostcoder @  28.5.2012,  10:02 Найти цитируемый пост)
значит тест на сравнение байтового массива для таких типов некорректен? 

да

Цитата(borisbn @  28.5.2012,  10:09 Найти цитируемый пост)
а ведь проблема именно в long double.
c float и с double - всё в порядке.

просто не проявляется в этих условиях..

Добавлено через 1 минуту и 21 секунду
Цитата(boostcoder @  28.5.2012,  10:12 Найти цитируемый пост)

так может вовсе запретить использование 'long double' ? или оставить на страх и риск юзеров? 


зачем запрещать если оно делает, что требуется ? 

Автор: boostcoder 28.5.2012, 11:21
Цитата(mes @  28.5.2012,  11:16 Найти цитируемый пост)
зачем запрещать если оно делает, что требуется ?

что-то я не уверен, что подразумевается под "оно делает, что требуется" smile 

Автор: mes 28.5.2012, 12:02
Цитата(boostcoder @  28.5.2012,  10:21 Найти цитируемый пост)
что-то я не уверен, что подразумевается под "оно делает, что требуется"

что именно смущает ?

Автор: boostcoder 28.5.2012, 12:03
неопределенность.
получить бы еще ссылку на стандарт, где говорится об этой ситуации..

Автор: mes 28.5.2012, 12:05
различие бинарного представления ? 

Цитата

Очевидно, что таким образом одно и то же число можно представить по-разному. Рассмотрим пример с длиной мантиссы |M|=4. Число «2» можно представить в следующем виде: 

2 = 10 (в двоичной системе) = 1.000e+1 = 0.100e+2 = 0.010e+3. 

http://habrahabr.ru/post/112953/

Добавлено @ 12:05
Цитата(boostcoder @  28.5.2012,  11:03 Найти цитируемый пост)
получить бы еще ссылку на стандарт, где говорится об этой ситуации.


гугль по IEEE 754

Добавлено через 2 минуты и 39 секунд
Цитата(boostcoder @  28.5.2012,  11:03 Найти цитируемый пост)
неопределенность.

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

Автор: boostcoder 28.5.2012, 12:11
ааа, тогда получается все нормально smile 

всем спасибо.
вопрос закрыт.

Автор: borisbn 28.5.2012, 12:17
Цитата(mes @  28.5.2012,  12:05 Найти цитируемый пост)
так что запретить придется даже флоат 

эт точно. сделай вместо
Цитата
lda1[i] = idx;

Код
lda1[i] = idx * 100 + idx / 3.0;

и получишь отличие уже на float
http://liveworkspace.org/code/07bd523290435fce3118e1e6853568cc

Автор: boostcoder 28.5.2012, 12:27
Цитата(borisbn @  28.5.2012,  12:17 Найти цитируемый пост)
получишь отличие уже на float

ага. вижу. ясно.

Автор: Alexeis 28.5.2012, 13:17
Цитата(mes @  28.5.2012,  13:05 Найти цитируемый пост)
Очевидно, что таким образом одно и то же число можно представить по-разному. Рассмотрим пример с длиной мантиссы |M|=4. Число «2» можно представить в следующем виде: 

2 = 10 (в двоичной системе) = 1.000e+1 = 0.100e+2 = 0.010e+3. 

  Это актуально только для текстового представления. Бинарно же не будет вариаций типа 
1.000e+1 / 0.100e+2 / 0.010e+3 , потому как числа с плавающей точкой не хранят позицию запятой и всегда сохраняются в одной форме. Думаю если сериализовать в бинарную форму, то после десериализации получим данные в точности.

Автор: borisbn 28.5.2012, 13:32
Цитата(Alexeis @  28.5.2012,  13:17 Найти цитируемый пост)
Думаю если сериализовать в бинарную форму, то после десериализации получим данные в точности

Да. Бинарная сеарелизация всё решит. Однако, совпадать будут только значения массива arr[ i ]-тые, а memcmp также может не совпадать.
Я сделал, как Вы и говорили (заполнил массивы только не 0101010, а первый 0x00, а второй 0xFF). Вот рез-ты
http://liveworkspace.org/code/15f9eee0856135c101d39b81dc15fa51

хмм. обнаружил интересную деталь: обычное присвоение long double (т.е. если вместо вытаскивания из stringstream'а сделать lda2[i] = lda1[i] ) копирует весь long double включая 0x00 на "незаполненных" местах, а вытаскивание из stingstream'а - нет

Автор: boostcoder 28.5.2012, 14:11
Цитата(borisbn @  28.5.2012,  13:32 Найти цитируемый пост)
обнаружил интересную деталь: обычное присвоение long double (т.е. если вместо вытаскивания из stringstream'а сделать lda2[i] = lda1[i] ) копирует весь long double включая 0x00 на "незаполненных" местах, а вытаскивание из stingstream'а - нет

мдя.. smile

Автор: mes 28.5.2012, 14:38
Цитата(Alexeis @  28.5.2012,  12:17 Найти цитируемый пост)
 Это актуально только для текстового представления. Бинарно же не будет вариаций типа 

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

Цитата(borisbn @  28.5.2012,  12:32 Найти цитируемый пост)
 копирует весь long double включая 0x00 на "незаполненных" местах, а вытаскивание из stingstream'а - нет

не понял .. копирование просто копирует бинарное представление, а стрим конвертирует  в/ из строки.. в чем нестыковка то ?

Автор: Alexeis 28.5.2012, 14:51
Цитата(borisbn @  28.5.2012,  14:32 Найти цитируемый пост)
Я сделал, как Вы и говорили (заполнил массивы только не 0101010, а первый 0x00, а второй 0xFF). Вот рез-ты
http://liveworkspace.org/code/15f9eee08561...1d39b81dc15fa51

  Я вижу только 2 байта заполненные FFFF вместо 6ти...
  Аа ну да. Этот компилятор определяет sizeof(long double) как 12 байт. 12 - 10 = 2 .  Тогда вроде все как и ожидалось.
  

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)