[vb6] convertir une variable double en large int

Bonjour,

Je cherche à appeler une api (setfilepointer) en passant en paramètre un nombre dépassant largement la capacité du type long (le paramètre lDistanceToMove) en vb6.

Pour remédier à ça, l’api propose un deuxième paramètre (lpDistanceToMoveHigh) permettant l’appel avec un nombre de 64 bits cette fois (et cette fois c’est suffisant :smiley: ).

Le problème c’est qu’il me faut convertir un nombre entier (par ex 6 471 496 248) en large integer (qui n’est pas vraiment un type mais deux variables de type long, une de poids faible et une de poids forte) et que je ne vois pas trop, même en cherchant sur le net, quelle équation utiliser pour obtenir ça).

Le seul truc que j’ai trouvé pour le moment, c’est de convertir le gros nombre entier en hexa, de récupérer les 8 derniers octets avec un mid() pour le poid faible et les autres pour le poids fort en reconvertissant ces deux chaînes de l’hexa en decimal… en clair c’est pas super et je préférerai un code basé que sur le calcul, sans chaînes…

:jap:


unsigned __int64 foo;

  // context de stockage d'une valeur
  blah((LARGE_INTEGER *) &foo);

oui mais je cherche ce code en vb6 :smiley:

D’où l’interêt de mettre le langage dans le titre comme spécifié dans la charte scotchée en haut du forum :wink:


dim x as double

  x = foo.lowpart
  x = x * 4294967296
  x = x + foo.highpart

Pas sur que ça marche :neutre:

Désolé c’est vrai c’était pas clair, c’est corrigé :wink:

Dans ton code à quoi correspond "foo"?

Car c’est justement high et low que je veux récupérer à partir d’un double :

Public type LARGE_INTEGER
    lngLow as long
    lngHigh as long
End type

Public function convertDblToLInt(offset as double) as LARGE_INTEGER

   dim usrLInt as LARGE_INTEGER
   usrLInt.lngLow = ??
   usrLInt.lngHigh = ??
   convertDblToLInt = usrLInt

End function

foo est une variable de type LARGE_INTEGER, lowpart et highpart sont les bon noms des 2 membres de la structure LARGE_INTEGER.

ok mais en fait ce que je veux c’est l’inverse :smiley: :

dim x as double

 x = foo.lowpart
 x = x * 4294967296
 x = x + foo.highpart

Avec ce code on obtient un entier de type double à partir de la structure LARGE_INTEGER déjà initialisée, moi j’essai d’obtenir les valeurs de cette structure à partir d’un entier de type double défini (avec une fonction semblable à celle que j’ai écrite au dessus).

Tu peux laisser windows le faire de manière implicite en faisant un RtlCopyMemory(var.LARGE_INTERGER, var.Double, lenB(var.double)).

Avec RtlMoveMemory y’a du mieux car j’ai des valeurs en retour mais ce sont des valeurs qui ne correspondent à rien (nombres à virgules, négatif…) :smiley:

Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
    
Public Type LARGE_INTEGER
   lowpart As Long
   highpart As Long
End Type

Public Function convertDblToLInt(offset As Double) As LARGE_INTEGER

    Dim foo As LARGE_INTEGER
    Dim x As Double
    
    x = 5
    Call CopyMemory(foo, x, LenB(x))
    
    MsgBox foo.lowpart
    MsgBox foo.highpart
    
End Function

Call CopyMemory(foo, ByVal x, LenB(x))

Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
    
Public Type LARGE_INTEGER
   lowpart As Long
   highpart As Long
End Type

Public Function convertDblToLInt(offset As Double) As LARGE_INTEGER

    Dim foo As LARGE_INTEGER
    Dim x As Double
    
    x = 8242244242#
    Call CopyMemory(foo, ByVal x, LenB(x))
    
    MsgBox foo.lowpart
    MsgBox foo.highpart
    
End Function

Ca me fait planter l’IDE à l’execution (la mémoire ne peut être “read” blablabla) :confused:

J’ai réinstallé VB, le premier, sans le ByVal est le bon, tu as des chiffres négatifs car VB traites les doubles en tant que type signé alors que dans LARGE_INTEGER ils sont non signés donc pour récupérer la bonne valeur, il suffit d’utiliser l’opérateur binaire AND comme suit :


   MsgBox foo.lowpart And &H7FFFFFFF
   MsgBox foo.highpart

Merci beaucoup d’avoir réinstallé vb :slight_smile: Mais le problème subsiste :frowning:

Avec x à 5, j’ai un lowpart à 0 et un highpart à 1075052544.
Avec x à 8145784147, j’ai un lowpart à 1429209088 (au lieu de 1) et un highpart à 1107187820 (au lieu de 3850816851).

Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
   
Public Type LARGE_INTEGER
  lowpart As Long
  highpart As Long
End Type

Public Function convertDblToLInt(offset As Double) As LARGE_INTEGER

   Dim foo As LARGE_INTEGER
   Dim x As Double
   
   x = 5
   Call CopyMemory(foo, x, LenB(x))
   
   MsgBox foo.lowpart And &H7FFFFFFF    ' <= Donne 0
   MsgBox foo.highpart                  ' <= Donne 1075052544
   
End Function

Bonjour,
ça ne marche pas de directement copier en mémoire un double dans un int, parce qu’il ne sont pas encodés de la même manière: dans un double, on garde de la place pour les décimales, et surtout, des bits sont réservé à la partie exposant.
(voir le petit article sur la partie mantisse-exposant)
http://fr.wikipedia.org/wiki/Mantisse

bref: ton ecodage hexa du début faisait exactement ce que tu voulait, sinon, il faut faire le calcul à la main: chaque entier 32bit est codé en gros sur 4milliards (0x100000000) donc il faut prendre le double, faire une division entière par 0x100000000, le reste représente le poid faible, le résultat représente le poid fort.

maintenant comme tu ne peut pas faire cette division avec des entiers, essaye avec des doubles, et pour obtenir le reste, tu enlève la partie entière du résultat de la division multiplié par le quotient.
:slight_smile:

edit: ça peut être un challenge aussi de ne faire que des opérations bits à bits à partir de la réprésentation flottante (norme IEEE 754)
http://fr.wikipedia.org/wiki/Virgule_flottante

en séparant la partie exposant du nombre puis en faisant des décalage de bits donné par l’exposant…


Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
 
Private Type LARGE_INTEGER
 lowpart As Long
 highpart As Long
End Type

Private Function RightShift(ByVal plValue As Long, piTimes As Integer) As Long
    plValue = CLng(plValue \ (2 ^ piTimes))
    RightShift = plValue
End Function

Private Function convertDblToLInt(ByVal value As Double) As LARGE_INTEGER
Dim foo As LARGE_INTEGER

    foo.lowpart = value And &HFFFFFFFF
    foo.highpart = RightShift(value, 16)
    convertDblToLInt = foo

End Function

Private Function convertLargeIntToDouble(ByVal low_part As Long, ByVal high_part As Long) As Double
Dim ret As Double

    ret = high_part
    If high_part < 0 Then ret = ret + 2 ^ 32
    ret = ret * 2 ^ 32
    
    ret = ret + low_part
    If low_part < 0 Then ret = ret + 2 ^ 32
    convertLargeIntToDouble = ret

End Function

Private Sub Form_Load()
Dim x As Double
Dim y As LARGE_INTEGER
Dim z As Double

    x = 5
    y = convertDblToLInt(x)
    z = convertLargeIntToDouble(y.lowpart, y.highpart)
    
    Debug.Print y.lowpart
    Debug.Print y.highpart
    Debug.Print z
    
End Sub

KarLKoX >> Ca marche avec 5 mais pas avec 5 000 000 000 (dépassement de capacité sur “foo.lowpart = value And &HFFFFFFFF”) :confused:
deltree >> Là je pense que ça sort de mes compétences :D, y’aurai t’il alors moyen de faire ça en une ligne de calcul?

Je comprend pas l’erreur, j’ai convertis rapido dans un autre langage et foo.lowpart = value And &HFFFFFFFF rentre bien dans un long … une des raisons pour laquelle j’ai quitté ce langage.
Je pourrais pas t’aider plus malheureusement.

Il doit pourtant bien y’avoir moyen :’( ton code me parait assez près du but pour tourner avec vb6!

J’y travaille :smiley:
c’est pourtant juste des maths, j’ai pas été clair :ane:, bon c’est plus simple avec le code
[codebox]
Public Sub cDoubleToLong()
Dim l As Long
Dim h As Long
Dim x As Double
'5 milliards
x = 5000000000#
MsgBox “x=” & x
l = x / (4294967296#)
MsgBox “l=” & l
h = x - (l * 4294967296#)
MsgBox “h=” & h
End Sub
[/codebox]
ça , ça marche :wink:
mais c’est avec des opérations sur flottants: tu fait les division en mode double, et tu stocke dans du long, ça te converti en entier.

Je travaille à la méthode bit à bit, mais comme je disais, ça dépend de la représentation machine et pour illustrer cela, j’utilise ce code
[codebox]
Public Declare Sub CopyMemory Lib “kernel32” Alias “RtlMoveMemory” (Destination As Any, Source As Any, ByVal Length As Long)

Public Type LARGE_INTEGER
lngLow As Long
lngHigh As Long
End Type

Public Sub cDoubleToLongBits()
’ en Double : Signe Exposant Mantisse = 1 bit 11 bits 52 bits
Dim l As Long
Dim h As Long
Dim x As Double
Dim bytes As LARGE_INTEGER

'5 milliards
'x = 5000000000#
x = 5
'x = 1

Call CopyMemory(bytes, x, 8)

MsgBox “x=” & x
'j’affiche les bytes pour comprendre le fonctionnement
MsgBox “b1=” & bytes.lngLow

MsgBox "b2=" & bytes.lngHigh

End Sub
[/codebox]
et en exécutant ça, on obtien le résultat suivant:
[codebox]
---- résultat:

Soit x=1

le byte b2=1072693248 soit en binaire:
00111111111100000000000000000000

décomposition des bits
s e m 1 11 52

S
eeeeeeeeeee
mmmmmmmmmmmmmmmmmmmm
00111111111100000000000000000000

01234567890123456789012345678901

x vaut 1 car (-1)^s*(1+m)+2^(e-1023) =
(-1)^0*(1+0)*2^(1023 - 1023) = 1
note e= 1111111111binaire = 1023 décimal


soit x=5
b2=1075052544
S
eeeeeeeeeee
mmmmmmmmmmmmmmmmmmmm
01000000000101000000000000000000

e=1025
m=0.25 (0.01 en binaire)
x vaut 5 car (-1)^s*(1+m)+2^(e-1023) =
(-1)^0*(1+0.25)*2^(1025 - 1023) = (1.25).2^2
note e= 10000000001binaire = 1023 décimal
[/codebox]

on voit que la représentation d’un simple “1” est une valeur:
signe=0
exposant=1111111111 en binaire (implicitement 1023 - cette valeur)
mantisse= 000000000 en binaire (implicitement 1+ cette valeur)

il y a un peu de travail à faire pour s’y retrouver, si j’ai du temps je m’y replonge, mais sinon, tu as la méthode “flottante” qui marche très bien :wink:

Ben quoi, j’ai floodé un peu pour rentrer dans les détails, mais ça marche mon truc :neutre:
ça t’affiche le poids fort et poids faible… est-ce que ça te va?
:slight_smile:

[quote=""]

[codebox]
Public Sub cDoubleToLong()
Dim l As Long
Dim h As Long
Dim x As Double
'5 milliards
x = 5000000000#
MsgBox “x=” & x
l = x / (4294967296#)
MsgBox “l=” & l
h = x - (l * 4294967296#)
MsgBox “h=” & h
End Sub
[/codebox]

[quote=""]