Page 1 sur 1

[Résolu] Opérateurs binaires et Floats

Publié : ven. 25/juil./2008 14:57
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

Re: Opérateurs binaires et Floats

Publié : ven. 25/juil./2008 15:20
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)

Publié : ven. 25/juil./2008 15:35
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 

Publié : ven. 25/juil./2008 15:52
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)...

Publié : sam. 26/juil./2008 9:56
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...

Publié : sam. 26/juil./2008 10:05
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?

Publié : sam. 26/juil./2008 10:53
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!

Publié : sam. 26/juil./2008 19:31
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.

Publié : dim. 27/juil./2008 16:08
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 ?

Publié : dim. 27/juil./2008 17:16
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.

Publié : lun. 28/juil./2008 11:02
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!