Zapisanie 3 wartości dla int[3] za pomocą rzutowania na long double

0

Głowię się nad pewnym zadaniem.

Kod:

#include <iostream>
using namespace std;

int main() {
    // Tablica z datą {ROK, MIESIAC, DZIEN}
    int date[3];

    date[0] = 2011; // Rok
    date[1] = 10; // Miesiac
    date[2] = 29; // Dzien

    // Wyświetlamy datę
    cout << date[0] << "/" << date[1] << "/" << date[2] << endl;

    // Wskaźnik bez definiowania typu (?)
    void * ptr = &date;
    long double *tmp = static_cast<long double*>(ptr);
    *tmp = (10000*2011)+(10000*11)+10; // coś źle myśle :)

    // Wyświetlamy datę
    cout << date[0] << "/" << date[1] << "/" << date[2] << endl;

    return 0;
}

Wynik:

2011/10/29
0/-1706806016/16407

Oczekiwany wynik:

2011/10/29
2011/11/10

Chodzi o pewien trick. Mamy 3 elementową tablicę typu int. Każdy element przechowuje datę, odpowiednio rok, miesiąc, dzień.
Typ int ma 4 bajty, więc cała tablica zapisana jest na 12 bajtach. Zadanie polega na zapisaniu daty za pomocą jednej definicji przypisania, zamiast trzech. Trzeba tutaj skorzystać z pewnej własności, że typ long double ma wielkość 12 bajtów. Pomyślałem, że można użyć do tego wskaźnik bez definiowania typu lub ewentualnie zrobić static_cast <long double="double">. Problem w tym, że nie wiem jak się za to zabrać i jak zapisać liczbę (rok miesiac dzien) tak by wskoczyła do odpowiednich komórek pamięci w tablicy.

0

Nie lepiej do bufora?

char buffor[12];

memcpy(buffor,&date[0],sizeof(int));
memcpy(buffor + 4,&date[1],sizeof(int));
memcpy(buffor + 8,&date[2],sizeof(int));
0

Chodzi o to że ma być 1 instrukcja przypisania a nie 3.

0
#include <iostream>
using namespace std;

int main() {
        short int * date;
        int rok=2011, mies=10, dzien=29;
        long long int D = (dzien*65536LLU+mies)*65536+rok;
        date=(short*)&D;
        cout << D<<' '<<sizeof(D)<<'\n';       //  <----------- tu trochę magii
        cout << date[0] << "/" << date[1] << "/" << date[2] << endl;    
        return 0;
} 

tak jak jest, otrzymamy

124554708955 8
2011/10/29 

bez zaznaczonego wiersza-8204/-18596/-13396Zbyt optymistyczny optymizator? (ideone jako C++)

0
*(long double*)(data) = *(long double*)(innadata);
0

Założenie co do zadania jest takie, że ma to być tablica int[3] i w instrukcji zapisu mamy wykorzystać long double (wiedząc, że sizeof(long double) = 12).

 #include <iostream>
using namespace std;

int main() {
    // Tablica z datą {ROK, MIESIAC, DZIEN}
    int date[3];

    date[0] = 2011; // Rok
    date[1] = 11; // Miesiac
    date[2] = 29; // Dzien

    // Wyświetlamy datę
    cout << date[0] << "/" << date[1] << "/" << date[2] << endl;

    // Nowa data
    int dzien = 20;
    int miesiac = 12;
    int rok = 2012;

    *(long double*)(&date) = ((dzien*4294967295+miesiac)*4294967295+rok); // Tutaj jest coś nie tak

    // Wyświetlamy datę
    cout << date[0] << "/" << date[1] << "/" << date[2] << endl;

    return 0;
}

Wynik

2011/11/29
-536870912/-536870976/49187

@Xitami na pewno jest sposób zrobienia tego bez "magii", trzeba tylko wykonać prawidłowo mnożenie liczb.

0

Nie możesz tak robić. Wykonując to mnożenie pozostaje Ci liczba całkowita, która jest konwertowana do liczby long double, dodatkowo pewnie będzie tam przepełnienie, czyli wynik może być dowolny (o ile dobrze pamiętam jest to niezdefiniowane).

Magia w sposobie Xitami polega na tym, że jak masz dwa wskaźniki niekompatybilnych typów do jednej wartości, to zmiana tej wartości przez jeden wskaźnik nie gwarantuje natychmiastowej zmiany wartości przy dereferencji drugiego wskaźnika. Gdyby takiego zachowania nie było, to kompilator by miał utrudnioną operację, praktycznie każda instrukcja mogłaby zmieniać wartości wskazywane przez wskaźnik. Uniemożliwiało by to optymalizacje przez trzymanie wartości w rejestrach, co jest szczególnie ważne na coraz popularniejszych architekturach RISC-owych (choć x86-64 też ma sporo rejestrów). Wywołanie funkcji wyświetlającej dane najprawdopodobniej wymusza wczytanie wartości z pamięci, więc pomaga w tej sytuacji.

Przy tym problemie musisz zobaczyć jak liczby są przechowywane w long double i ewentualnie zastosować bardziej skomplikowane operacje matematyczne, niż samo mnożenie. Zacznij od wikipedii i zastanów jak wprowadzić odpowiednie wartości w odpowiednie miejsca long double (pamiętaj o endianess). Ogólnie pomysł na takie ustawienie daty jest chory, nieprzenośny oraz niepotrzebnie skomplikowany. Jeżeli byś pisał w taki sposób w kodzie produkcyjnym, to jakiś psychopata w końcu ciebie dopadnie.

0

kkardasz
twoje "prawidłowo" wygląda tak: dzien*4294967295, sprawdź ile to jest
(2^32 = 4294967296, wtedy otrzymasz okrąglutkie zero) [razem dwa błędy]

druga sprawa to chcesz by wskaźnik wskazywał na wynik wyrażenia, czyli któryś rejestr (w sumie możliwe, są i takie procesory) w którym nie wiadomo co będzie za chwilę? [błąd trzeci]

Zjarek: "...najprawdopodobniej wymusza wczytanie wartości..."
możliwe, że dzięki temu w ogóle wykonana przypisanie do "date" czegokolwiek.

0

dokładnie tak jak Ci pokazałem, a nie modyfikujesz kod który Ci napisałem:

#include <iostream>
using namespace std;

int main()
{
  // Tablica z datą {ROK, MIESIAC, DZIEN}
  int date[3];

  date[0] = 2011; // Rok
  date[1] = 11; // Miesiac
  date[2] = 29; // Dzien

  // Wyświetlamy datę
  cout << date[0] << "/" << date[1] << "/" << date[2] << endl;

  // Nowa data
  int dzien = 20;
  int miesiac = 12;
  int rok = 2012;
  int tab[3] = {rok,miesiac,dzien};

  *(long double*)(date) = *(long double*)(tab);

  // Wyświetlamy datę
  cout << date[0] << "/" << date[1] << "/" << date[2] << endl;

  return 0;
}
0

a zwróciłeś uwagę na to o czym mówiliśmy? wynik zależy od kompilatora/opcyi onego.
http://ideone.com/I5XIz

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0