[Résolu] Opérateurs binaires et Floats

Vous débutez et vous avez besoin d'aide ? N'hésitez pas à poser vos questions
kelebrindae
Messages : 579
Inscription : ven. 11/mai/2007 15:21

[Résolu] Opérateurs binaires et Floats

Message par kelebrindae »

Bonjour,

Je suis en train d'essayer de convertir ce petit bout de code en PureBasic:

Code : Tout sélectionner

     * method  noise3D
     * description  Creates noise using three passed arguments, prime numbers, and  bitwise XOR operations.
     * param   x   (Number)  -- a real number.
     * param   y   (Number)  -- a real number.
     * param   z   (Number)  -- a real number.
     * return  (Number)  -- returns the resulting noise value.
    **/
    static function noise3D(x:Number,y:Number,z:Number):Number
    {
        var n:Number;

        n = Math.floor(x+y*57+z*131);
        n = (n<<13)^n;

        return (1.000-((n*(n*n*15731+789221)+1376312589) & 0x7FFFFFFF)*0.000000000931322574615478515625);
    }
J'obtiens ceci:

Code : Tout sélectionner

Procedure.f noise3d(x.f,y.f,z.f)

  Protected n.f
 
  n=Int(x+y*57+z*131)
  n=Pow(n<<13,n)

  ProcedureReturn (1.000-((n*(n*n*15731+789221)+1376312589) & $7FFFFFFF)*0.000000000931322574615478515625)

EndProcedure
Et c'est là que je suis bloqué, pour deux raisons:
- les opérateurs binaires de PB ne fonctionnent pas avec des floats
- si je ne mets pas de floats, les valeurs dépassent la capacité des entiers (notamment Pow(n<<13,n) qui est très grand)

Quelqu'un peut-il m'aider ? Merci d'avance!

PS: pour info, il s'agit d'un générateur de nombres pseudo-aléatoires; pour les trois valeurs en entrée, il génère un résultat entre -1 et 1 qui ressemble à du hasard, mais qui est toujours le même pour ces trois valeurs
Dernière modification par kelebrindae le lun. 28/juil./2008 11:03, modifié 1 fois.
Backup
Messages : 14526
Inscription : lun. 26/avr./2004 0:40

Re: Opérateurs binaires et Floats

Message par Backup »

kelebrindae a écrit : PS: pour info, il s'agit d'un générateur de nombres pseudo-aléatoires; pour les trois valeurs en entrée, il génère un résultat entre -1 et 1 qui ressemble à du hasard, mais qui est toujours le même pour ces trois valeurs
bref comme Random() quoi :)

voici ce que ça donne en Purebasic

Code : Tout sélectionner


Procedure rand(a,b,c)   
      RandomSeed(a+b+c)  
      a1=Random(a)
      b1=Random(b)
      c1=Random(c) 

      ProcedureReturn a1+b1+c1
EndProcedure


Debug rand(5,5,3)
Dernière modification par Backup le ven. 25/juil./2008 15:39, modifié 1 fois.
Avatar de l’utilisateur
djes
Messages : 4252
Inscription : ven. 11/févr./2005 17:34
Localisation : Arras, France

Message par djes »

A l'arrache, pas sûr que ça fonctionne, mais ça te mettra sur la piste.

Code : Tout sélectionner

Procedure.f noise3d(x.d, y.d, z.d)

  Protected n.q, m.l, o.d
 
  n = x + ( y * 57 ) + ( z * 131 )
  n = Pow( n * 8192, n )
  
  n = n * ( n * n * 15731 + 789221 ) + 1376312589
  m = n & $7FFFFFFF
  o = m / 1000000
  o = o * 0.0009313225746154785

  ProcedureReturn ( 1.0 - o )

EndProcedure 
kelebrindae
Messages : 579
Inscription : ven. 11/mai/2007 15:21

Message par kelebrindae »

@Dobro:

J'y ai pensé mais ça ne convient pas :( :
- RandomSeed ne prend pas de floats en entrée (ce que fait la proc que j'essaie de traduire);
- le randomSeed n'a qu'un paramètre là où il m'en faudrait 3;
- le RandomSeed à chaque appel est par ailleurs assez lent (sur mon PC, 4x plus lent qu'une série compliquée d'opérations bitwises + arithmétiques semblable à celle que j'ai cité);

Et puis j'aimerais me passer du Random car je le soupçonne d'être lié au Hardware (donc différent d'une machine à l'autre)...
kelebrindae
Messages : 579
Inscription : ven. 11/mai/2007 15:21

Message par kelebrindae »

@Djes:
Merci! ça vaut le coup d'essayer.
Je ne suis pas certain que "n<<13" équivaut à "n*8192" (l'opérateur << tronque les bits qui dépassent vers la gauche, non?), et il me semble que même en quad, ce sera un peut juste pour stocker "n = Pow( n * 8192, n )".

D'après le peu que je comprends, j'ai l'impression justement que l'astuce est d'obtenir des nombres très grands en float, puis de tronquer leur représentation binaire pour obtenir un nombre complètement différent... Mais là je raconte probablement n'importe quoi, parce que j'suis une bille avec les opérateurs bitwise... :oops: Tu es sans doute infiniment plus à l'aise que moi dans ce domaine, avec ta connaissance de l'assembleur!

Pfff! 'faut vraiment que je m'y mette...
kelebrindae
Messages : 579
Inscription : ven. 11/mai/2007 15:21

Message par kelebrindae »

@Djes:

Oui, c'est ce que je pensais: un debug juste après "n = Pow( n * 8192, n )" donne toujours:

Code : Tout sélectionner

n=-9223372036854775808
soit la valeur maxi d'un quad... C'est pour cela que l'algo d'origine utilise un float, je suppose.

Je continue à chercher; peut-être en reproduisant manuellement les opérations bitwise? C'est possible, ça?
kelebrindae
Messages : 579
Inscription : ven. 11/mai/2007 15:21

Message par kelebrindae »

Djes, j'ai peut-être trouvé un élément de réponse dans un de tes posts de 2005!
Puisque les opérateurs bitwise, comme leur nom l'indique, ne considèrent que les bits (et pas le fait que la valeurs soit du Long ou du Float), il est peut-être possible de convertir la formule directement en remplaçant les occurrences de x, y, ou z par PeekL(@x), PeekL(@y) et PeekL(@z).

Je jette un oeil à ça tout de suite!
Avatar de l’utilisateur
djes
Messages : 4252
Inscription : ven. 11/févr./2005 17:34
Localisation : Arras, France

Message par djes »

Oui, c'est une bonne idée, peut-être que le décalage permet de faire passer la mantisse dans la partie entière. Dans ce cas, une copie directement dans un nombre entier ferait l'affaire. Sinon tu peux faire tout ça en assembleur, mais il faudrait avoir le code compilé de l'autre côté pour être sûr que le résultat sera le même.
kelebrindae
Messages : 579
Inscription : ven. 11/mai/2007 15:21

Message par kelebrindae »

Bon, voilà où j'en suis:
(en commentaire, la ligne à traduire; juste en dessous, la traduction)

Code : Tout sélectionner

  Protected n.f,n2.f
  Protected n3.l
 
  ; " n = Math.floor(x+y*57+z*131); "
  n=Int(x+y*57+z*131)

  ; " n = (n<<13)^n "
  n3=PeekL(@n)<<13
  n2=PeekF(@n3)
  n=Pow(n2,n)

  ; " (n*(n*n*15731+789221)+1376312589) & 0x7FFFFFFF "
  n=(n*(n*n*15731+789221)+1376312589)
  n3=PeekL(@n) & $7FFFFFFF
  n=PeekF(@n3)

  ; " return (1.000-n*0.000000000931322574615478515625) "
  ProcedureReturn (1.000-n/1073741824.0)
Le problème, c'est que "Pow(n2,n)" me retourne presque systématiquement "1.#INF00". Je ne sais pas trop ce que ça veut dire, mais je suppose que c'est parce que la valeur est trop grande...

:x AAAAARGH! ça commence à devenir frustrant! :x

Quant à l'assembleur, je n'y pense même pas: je n'y connais rien.
Djes, toi qui fréquentes le forum ASM, tu crois qu'une bonne âme accepterait de faire la traduction de l'algo pour moi si je demande gentiment ?
Avatar de l’utilisateur
djes
Messages : 4252
Inscription : ven. 11/févr./2005 17:34
Localisation : Arras, France

Message par djes »

Ca ne coute rien d'essayer. Si tu pouvais fournir un code complet, ce serait mieux. Et puis comme je te l'ai dit, si on avait la version exécutable, on pourrait la désassembler directement, ce serait plus efficace.
kelebrindae
Messages : 579
Inscription : ven. 11/mai/2007 15:21

Message par kelebrindae »

La persévérance (ou le fait d'être complètement borné) finit toujours pas payer.
En lançant une N-ième recherche sur Google, j'ai fini par trouver des forums (russes et allemands, en Delphi je crois) qui parlait de mon problème.

Dans l'algo d'origine, le "(n<<13)^n" table sur le fait que la valeur "boucle" en C (on revient à zéro quand on dépasse la limite du format) alors qu'elle plafonne en PB (on reste à la valeur maxi autorisée par le format).

Une astuce possible dans ce cas est de remplacer l'élévation à puissance n par l'opérateur binaire XOR.
=> "pow(n<<13,n)" devient "(n<<13)!n".

Grâce à cela, j'ai obtenu l'algo suivant, qui à l'air de donner ce que j'attendais:

Code : Tout sélectionner

Procedure.f noise3d(x.f,y.f,z.f)

  Protected n.l,n2.q
 
  ; " n = Math.floor(x+y*57+z*131); "
  n2=x+y*57+z*131
  If n2>2147483647 Or n2<-2147483648
    n=n2%2147483648
  Else
    n=n2
  EndIf
  ; (J'aurais pu me passer de n2.q, mais ça évite de plafonner trop vite dans le cas où x,y, ou z sont très grands)

  ; " n = (n<<13)^n "
  n=(n<<13)!n

  ; " (n*(n*n*15731+789221)+1376312589) & 0x7FFFFFFF "
  n=(n*(n*n*15731+789221)+1376312589) & $7FFFFFFF

  ; " return (1.000-n*0.000000000931322574615478515625) "
  ProcedureReturn (1.000-n/1073741824.0) 

EndProcedure
Et le plus beau, c'est que c'est quasiment aussi rapide que le "Random(16384)/8192 - 1" que j'utilisais à l'origine à la place de cette procédure! :D

Merci de votre aide!
Répondre