IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage Java Discussion :

Longs stockés comme des doubles


Sujet :

Langage Java

  1. #1
    Membre éprouvé
    Inscrit en
    Août 2010
    Messages
    1 124
    Détails du profil
    Informations forums :
    Inscription : Août 2010
    Messages : 1 124
    Points : 1 277
    Points
    1 277
    Par défaut Longs stockés comme des doubles
    Bonjour,

    Je suis conscient qu'un double ne peut contenir un long quelconque de manière bijective, lorsque ce long est stocké dans la partie entière du double.

    Par contre, il me semble que Double.longBitsToDouble et Double.doubleToRawLongBits permettent une bijection (vu que les 2 types primitifs sont stockés sur le même nombre de bits).
    - Pourriez vous confirmer que cette méthode est effectivement une bijection (cf exemple ci dessous)
    - Ce qui me choque, c'est que l'opération est compatible avec la somme de double (voir les 3 dernieres lignes ci dessous). Est-ce que je peux compter sur ce comportement (ie ne pas avoir a repasser en long pour sommer) ? Avec vous une explication pour ce phénomène ?

    Merci d'avance pour vos lumières.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
     
           public static long lng2lng(long x) {
    		// long y = Double.doubleToLongBits(Double.longBitsToDouble(x));
    		long y = Double.doubleToRawLongBits(Double.longBitsToDouble(x));
    		if (x !=y) System.out.printf("%s != %s !",x ,y);
    		return y;
    	}
    	public static void main(String[] args) {
     
    		lng2lng(Long.MAX_VALUE);
    		lng2lng(Long.MIN_VALUE);
    		lng2lng(1246l);
    		lng2lng(0);
    		lng2lng(-1);
     
    		System.out.println(Double.longBitsToDouble(1)); 
    		System.out.println(Double.longBitsToDouble(1000)); 
    		System.out.println(Double.doubleToRawLongBits(Double.longBitsToDouble(1)+Double.longBitsToDouble(1000))); // wahou !
     
     
    	}

  2. #2
    Expert éminent sénior
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 481
    Points : 48 806
    Points
    48 806
    Par défaut
    C'est quoi le but de la manoeuvre? Quel est pour toi l'intérêt d'aller déclarer un long comme un double?

    Sinon, pour répondre à tes questions: non ce n'est pas bijectif, il suffit de voir comment la méthode décrit son fonctionnement pour constater qu'il y a tout un range de bits qui sont transformés. Les NaN peuvent changer au niveau de la pattern sans prévenir.




    Et non l'addition ne marche pas comme ça. T'as juste du bol là parce que t'es resté dans le domaine restreint qui garde l'exposant et le signe de bits à 0.

    Donc pour résumé, non, non et c'est une idée à la con à oublier

  3. #3
    Membre éprouvé
    Inscrit en
    Août 2010
    Messages
    1 124
    Détails du profil
    Informations forums :
    Inscription : Août 2010
    Messages : 1 124
    Points : 1 277
    Points
    1 277
    Par défaut
    Hello Tchize_ et merci.

    Ok pour l'addition, ca m'étonnait aussi.

    Vu que les 2 types sont stockés sur le même nombre de bytes, il doit y avoir une bijection, ne serait-ce qu'à partir de la version serializée.
    J'ai quand même l'impression que cette transformation est bijective, de ce que j'ai pu en testé. Par contre elle semble dépendre de la JVM.

    En ce qui concerne le pourquoi, j'ai un problème de design que j'hésite à attaquer de manière propre. Je cherche à parraléliser des mappers sous Hadoop, cad:
    - j'ai un ensemble de "functions" : des classes héritant de Mapper<Ki,Vi,Ko,Vo>, pour plusieurs jeux de classes paramètriques <Ki,Vi,Ko,Vo>, qui définissent
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    map(Ki key, Vi value, Mapper<Ki,Vi,Ko,Vo>.Context ctx)
    Mon but est de créer un autre Mapper dont la méthode map() appele chaque Mapper.map() de mon ensemble, en lui passant un proxy du Context.
    Je ne sais trop comment gérer la multiplicité de types génériques possibles, vu que chaque map() que je dois appeler attends une classe générique concrète.
    Il est hors de question de paramétrer le mapper aggrégé par tous les types génériques des mappers wrappés (qui sont en nombre variables).

    En terme de performance, vu que les inputs/outputs de ces fonctions sont serialisées / deserialisées par hadoop, je me disais qu'interpréter un unique type primitif de multiples manières serait un bon compromis.

  4. #4
    Expert éminent sénior
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 481
    Points : 48 806
    Points
    48 806
    Par défaut
    Citation Envoyé par shaiHulud Voir le message
    Hello Tchize_ et merci.

    Ok pour l'addition, ca m'étonnait aussi.

    Vu que les 2 types sont stockés sur le même nombre de bytes, il doit y avoir une bijection, ne serait-ce qu'à partir de la version serializée.
    J'ai quand même l'impression que cette transformation est bijective, de ce que j'ai pu en testé. Par contre elle semble dépendre de la JVM.
    Est-ce que tu as lu la javadoc? Elle dit tout le contraire.

    Consequently, for some long values, doubleToRawLongBits(longBitsToDouble(start)) may not equal start.
    En ce qui concerne le pourquoi, j'ai un problème de design que j'hésite à attaquer de manière propre.
    Ben fourrer des bits dans un double, c'est pas propre.



    Mon but est de créer un autre Mapper dont la méthode map() appele chaque Mapper.map() de mon ensemble, en lui passant un proxy du Context.
    Je ne sais trop comment gérer la multiplicité de types génériques possibles, vu que chaque map() que je dois appeler attends une classe générique concrète.
    Il est hors de question de paramétrer le mapper aggrégé par tous les types génériques des mappers wrappés (qui sont en nombre variables).
    Si tu montrait le code de ton wrapper qui englobe tout là. Il peut arrriver régulièrement qu'on ne puisse pas tout typer explicitement en java avec les génériques. Ca arrive quand tu as une chaine de dépendances entre tes types et que cette chaine est différente pour chaque élément de la map. Dans ce cas il aut passer par des <?> et un ignore warning.

  5. #5
    Membre éprouvé
    Inscrit en
    Août 2010
    Messages
    1 124
    Détails du profil
    Informations forums :
    Inscription : Août 2010
    Messages : 1 124
    Points : 1 277
    Points
    1 277
    Par défaut
    Pourtant,
    http://docs.oracle.com/javase/7/docs...ngBits(double)
    doubleToRawLongBits()
    ...
    In all cases, the result is a long integer that, when given to the longBitsToDouble(long) method, will produce a floating-point value the same as the argument to doubleToRawLongBits.

  6. #6
    Expert éminent sénior
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 481
    Points : 48 806
    Points
    48 806
    Par défaut
    oui, ça c'est la relation double -> long -> double. On te dit que ça te donnera un double de la même valeur, pas que le champ de bits sera le même. Certains double on plusieurs réprésentations en bit, et c'est là ton problème.


    Toi tu parle de long -> double -> long et ça ce n'est pas garantis, au contraire.

  7. #7
    Membre éprouvé
    Inscrit en
    Août 2010
    Messages
    1 124
    Détails du profil
    Informations forums :
    Inscription : Août 2010
    Messages : 1 124
    Points : 1 277
    Points
    1 277
    Par défaut
    Merci pour la distinction ! Je peux donc au pire procéder dans l'autre sens (tout mettre dans des long). Et effectivement, ce n'est pas propre.

    Le bout de doc que tu cites est en plein milieu d'un paragraphe dédié aux NaNs, du coup je me demande si ca ne serait pas bijectif en se restreignant aux non-NaNs.
    Sauriez-vous construire un double non nans pour lequel ca ne marche pas ? (la boucle testant tous les long est assez lente à tourner )

    En ce qui concerne une solution propre, mon plus gros problème est de caster les arguments selon la fonction à laquelle je les envoie. Ils sont de type génériques déclarés dans les fonctions en question.
    Je ne vois pas d'autres solutions que
    - La reflection, dont je crains la pénalité sur la performance (je traite des mappers/reducers sur hadoop, donc il s'agit vraiment de la "boucle interne" que je ne voudrais pas trop alourdir.)
    - Paramétrer ma classe par tous les types des fonctions wrappés. Elles sont en nombre variables, donc je dois probablement passer par des Pair de Pair ...

  8. #8
    Expert éminent sénior
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 481
    Points : 48 806
    Points
    48 806
    Par défaut
    Citation Envoyé par shaiHulud Voir le message
    En ce qui concerne une solution propre, mon plus gros problème est de caster les arguments selon la fonction à laquelle je les envoie. Ils sont de type génériques déclarés dans les fonctions en question.
    Du code, du code, du code. On ne pourra pas t'aider avec juste du blabla. Tout ce qu'on peut te dire pour le moment c'est que stocker un long et un double ensemble, c'est une idée à la con. Sans parler du faire que t'aura de toutes façons toujours le même problème avec les autres types (byte, char, BigNumber, String, ...) et que tu ne résoud pas ton problème de base qui est de connaitre le type.

  9. #9
    Membre éprouvé
    Inscrit en
    Août 2010
    Messages
    1 124
    Détails du profil
    Informations forums :
    Inscription : Août 2010
    Messages : 1 124
    Points : 1 277
    Points
    1 277
    Par défaut
    J'ai N couples de mappers/reducers dont les inputs du mapper sont les mêmes. Par exemple pour N=2
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    class M1 extends Mapper<LongWritable, Text, Text, Text> {		
    	public void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {context.write(new Text("M1"),new Text("M1v"));}
    }
    class R1 extends Reducer<Text, Text, LongWritable, LongWritable> {
    	public void reduce(Text key, Iterable<Text> values, Context context) throws IOException, InterruptedException {context.write(new LongWritable(1), new LongWritable(1));}
    }
    class M2 extends Mapper<LongWritable, Text, IntWritable, IntWritable> {		
    	 public void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {context.write(new IntWritable(1),new IntWritable(2));}
    }
    class R2 extends Reducer<IntWritable, IntWritable, IntWritable, IntWritable> {
    	public void reduce(IntWritable key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {context.write(key, values.iterator().next());}
    }
    Je veux créer un mapper aggrégé qui apelle map() pour chacun des N mappers.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    class ParallelMapper extends Mapper<LongWritable, Text, Pair<Integer,Object>, Object> {		
        private List<Mapper<?,?,?,?> mappers; // contient la liste des mappers instanciés (ici {M1,M2}), probablement par reflection dans le setup(Context).
        public void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
            for (Mapper<> submap : mappers) { // appele chaque mapper avec un proxy de contexte qui "redirige" le write.
                submap.map(key,value,new IdMapContext(context, submap.identifier));
            }
        }
    }
    Vu que les outputs du n-ème mapper doit etre envoyé sur le reducer correspondant, je dois décorer la clef emise par un identifiant du mapper.
    J'ai le droit d'utiliser la classe de mon choix comme outputs pour effectuer cette décoration. Une version fruste consiste en une Pair<Integer,Object>. L'integer identifie l'indice n < N du mapper, la partie object est la clef émise naturellement par le mapper (Text pour M1, IntWritable pour M2).
    La "décoration" agit via un proxy de contexte que je passe à chaque mapper, typiquement
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    class IdMapContext extends Mapper<Ki,Vi,Ko,Vo>.Context {
        private Mapper<Ki,Vi,Pair<Integer,Object>, Object>.Context wrapped;
        public void write(Ko key, Vo value) {
            wrapped.write(new Pair<Integer,Object>(identifier, key), value);
        }
    }
    Le reducer doit dispactcher ses clefs au bon subreducer. Il doit également retirer la décoration de la clef (l'identifiant du mapper) pour que le reducer revoive la clef attendue. Il doit aussi donner au sous-reducer un proxy de contexte qui rajoute l'identifiant sur la clef en sortie.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    class ParallelReducer extends Reducer<Pair<Integer,Object>, Object, Pair<Integer,Object>, Object> {		
        private List<Reducer<?,?,?,?> reducers; // contient la liste des reducers instanciés (ici {R1,R2}), probablement par reflection dans le setup(Context).
        public void reduce(Pair<Integer,Object> key, Iterable<Object> values, Context context) throws IOException, InterruptedException {
            reducers.get(key.getFirst()).reduce(key.getSecond(), values, new IdRedContext(context, key.getFirst())); // cast issues
        }
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    class IdRedContext extends Reducer<Object, Object,Object, Object>.Context {
        private Reducer<Pair<Integer,Object>, Object,Pair<Integer,Object>, Object>.Context wrapped;
        public void write(Object key, Object value) {
            wrapped.write(new Pair<Integer,Object>(identifier, key), value);
        }
    }
    J'ai écrit tout ca avec Object pour l'instant, ce qui induit un problème de cast dans le ParallelReducer:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    reducers.get(redidx).reduce(key.getSecond(), values, new IdRedContext(context, key.getFirst())); // cast issues
    le reduce appelé ici est typé par générique dans R1 et R2 et attends dans mon exemple des clefs/value de type (LongWritable, Text) et (IntWritable,IntWritable).
    Il attends aussi un Reducer<...,LongWritable, Text>.Context et non un Reducer<Object, Object,Object, Object>.Context

    Je vois deux solutions:
    - de la reflection pour caster au bon type.
    - paramétrer toutes mes classes par tous les types générique des N mappers/reducers.
    La 1ere me fait peur pour les performances, la seconde pour la lisibilité/ facilité d'utilisation.

  10. #10
    Expert éminent sénior
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 481
    Points : 48 806
    Points
    48 806
    Par défaut
    Quand tu dis que tu as des Cast issue, c'est le compilateur qui refuse de compiler ou à l'exécution tu as un ClassCastException?

    Tu peux aussi nous montrer Mapper et Reducer, qu'on vois à quoi correspondent les 4 paramètres? Je m'inquiète par exemple de voir dans ton Mapper qu'il y a 4 paramètres générique mais qu'en fait seulement trois paramètres sont utilisés pour la seule méthode présente.

    Aussi, normalement, on n'est pas censé avoir besoin d'un classe concrète par groupe actuelle. Si tu prend List par exemple, elle est générique, mais on ne fais pas une sous classe d'ArrayList à chaque fois qu'on a besoin d'une liste différente. J'ai l'impression que tu essaie d'implémenter tes génériques à la C++

  11. #11
    Membre éprouvé
    Inscrit en
    Août 2010
    Messages
    1 124
    Détails du profil
    Informations forums :
    Inscription : Août 2010
    Messages : 1 124
    Points : 1 277
    Points
    1 277
    Par défaut
    Quand tu dis que tu as des Cast issue
    Ni l'un ni l'autre, puisque je n'ai pas encore écrit le cast. Pour ca j'ai besoin du type vers lequel caster (via l'une ou l'autre des pistes que j'envisage).
    pour que cette ligne fonctionne
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    reducers.get(redidx).reduce(key.getSecond(), values, new IdRedContext(context, key.getFirst()));
    il faut que je cast:
    * key.getSecond() de Object vers les types d'entrées de R1 ou R2,
    * values de Iterable<Object> vers Iterable<type d'entrée de R1 ou R1>
    * new IdRedContext() vers "R1.Context" ou "R2.Context".

    qu'on vois à quoi correspondent les 4 paramètres
    Pour Mapper<A,B,C,D>:
    * A,B correspondent aux types des 2 1ers arguments de map(A key, B value, Mapper<ABCD>.Context).
    * C,D sont les types des 2 1ers arguments de Mapper<ABCD>.Context.write(key C,value D))
    Pareil pour les Reducers, sauf que le 2nd argument de reduce est un Iterable<B> plutôt qu'un B.

    En ce qui concerne les classes concrètes, tu as raison, mon mapper devrait être paramétré par les types d'entrée.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    class ParallelMapper<A,B> extends Mapper<A, B, Pair<Integer,Object>, Object>
    Les types de sorties peuvent être remplacés par ce qu'il nous plaira pour réussir la parallélisation.

  12. #12
    Expert éminent sénior
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 481
    Points : 48 806
    Points
    48 806
    Par défaut
    c'est flou pour moi, donne un code d'exemple d'utilisation de ton mapper, de ton reducer, donne le code de ton context et explique son rôle. Je peux pas te conseiller avec des informations partielles

    J'aimerais aussi voir l'implémentation de cette interface, que je vois comment tes types sont manipulés.

    Tu n'a normalement pas besoin de caster vers les générique. Tu peux te contenter de prétendre au compiler que ce sont des objets.

  13. #13
    Expert éminent sénior
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 481
    Points : 48 806
    Points
    48 806
    Par défaut
    Au passage en relisant, pour éviter les problèmes de cast au niveau de la compilation, je ferais ceci pour ton reducer


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    class ParallelReducer extends Reducer<Pair<Integer,Object>, Object, Pair<Integer,Object>, Object> {		
        private List<Reducer> reducers; // contient la liste des reducers instanciés (ici {R1,R2}), probablement par reflection dans le setup(Context).
        public void reduce(Pair<Integer,Object> key, Iterable<Object> values, Context context) throws IOException, InterruptedException {
            reducers.get(key.getFirst()).reduce(key.getSecond(), values, new IdRedContext(context, key.getFirst())); 
        }
    }
    Comme le type n'est pas précisé, le compilateur laissera passer avec un warning. Tu va de toutes façons être coincé avec des warnings. Ta liste contient des types différents, et tu assume juste que la sortie de get() est compatibles avec ls autres paramètres que tu as recu. C'est à ton code de s'en assurer, le compilateur en est incapable.

  14. #14
    Membre éprouvé
    Inscrit en
    Août 2010
    Messages
    1 124
    Détails du profil
    Informations forums :
    Inscription : Août 2010
    Messages : 1 124
    Points : 1 277
    Points
    1 277
    Par défaut
    Les classes Mapper et Reducer proviennent du framework d'Hadoop. Il n'y a pas vraiment d'utilisation: je construis un Job en lui passant Mapper.class et Reducer.class. En simplifiant, lorsque ce job est run, Hadoop va instancier ces Mappers/Reducers dans chaque JVM, va appeler Mapper.map() dans chaque JVM sur chaque input disponible, serialiser les résultats et les envoyer au Reducer, et finalement écrire les outputs du Reducer dans des fichiers.

    Le contexte permet à hadoop de lire la configuration du Job. Dans le cas qui m'intéresse, la seule méthode à mocker est write():
    * Mapper.Context.write(K k,V v) signifie que (k,v) est un output du mapper que hadoop va envoyer au reducer.
    * Reducer.Context.write(K,V) ajoute une clef/valeur en output du job que Hadoop va rajouter en tant que ligne dans son fichier de sortie.

    L'intérêt de la parallélisation que j'essaie d'implémenter est de faire tourner plusieurs jobs indépendants avec une seule itération sur les données d'inputs (qui sont conséquentes et stockées sous HDFS).

    je ferais ceci pour ton reducer
    Que représente cette classe Context non paramétrée ? Context est une classe interne à Reducer<A,B,C,D>. Est ce que ta suggestion équivaut à Reducer<?,?,?,?>.Context, ou bien à ceux déclarés par le Reducer environnant ?

    le compilateur en est incapable
    Sauf peut etre si je paramétrise mes ParallelMapper / Reducer par tous les types génériques impliqués.

Discussions similaires

  1. IBExpert : troncature des double precision ?
    Par Magnus dans le forum Outils
    Réponses: 1
    Dernier message: 25/08/2005, 15h07
  2. [TList]: trier des doubles
    Par PpPool dans le forum Langage
    Réponses: 6
    Dernier message: 11/08/2005, 11h34
  3. [JPEG] Traiter des Jpg comme des Bmp : Canvas
    Par Clorish dans le forum Langage
    Réponses: 9
    Dernier message: 20/07/2005, 11h14
  4. Procédure stockée : liste des serveurs SQL disponibles
    Par AF_STjohn dans le forum MS SQL Server
    Réponses: 3
    Dernier message: 15/06/2005, 20h11
  5. [C/C++] afficher des doubles dans une message box
    Par lalaurie40 dans le forum MFC
    Réponses: 1
    Dernier message: 24/05/2005, 14h55

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo