2010-03-29 8 views
4

J'ai trouvé la question suivante Convert Delphi Real48 to C# double mais je veux aller dans l'autre sens, C# à Delphi.Convertir C# double à Delphi Real48

Est-ce que quelqu'un sait comment cela peut être fait? J'ai essayé le reverse engineering du code mais sans trop de chance.

Mise à jour:

Je suis après le code C# qui prendra un double et le convertir en un Real48 (octet [] de la taille 6).

Merci

+0

Quelle est la langue pour convertir - Delphi ou C#? – kludg

+1

Si ce serait Delphi, la solution serait "MyReal48Var: = MyDoubleVar;" ;-) –

Répondre

1

Si vous êtes familier avec C (comme vous écrivez en C# vous devriez être bien), consultez cette fonction. Le déplacer en C# ne devrait pas être trop difficile.

C'est plutôt moche, mais je pense que c'est nécessaire.

Référence: http://forums.ni.com/ni/board/message?board.id=60&message.id=3553

enum prconverr double_to_real (double d, real *r) 
    /* converts C double to Pascal real, returns error code */ 

{ 
    union doublearray da; 
    unsigned x; 

    da.d = d; 

    /* check for 0.0 */ 
    if ((da.a[0] == 0x0000) && 
     (da.a[1] == 0x0000) && 
     (da.a[2] == 0x0000) && 
     /* ignore sign bit */ 
     ((da.a[3] & 0x7FFF) == 0x0000)) { 
     /* exponent and significand are both 0, so value is 0.0 */ 
     (*r)[2] = (*r)[1] = (*r)[0] = 0x0000; 
     /* sign bit is ignored (-0.0 -> 0.0) */ 
     return prOK; 
    } 

    /* test for maximum exponent value */ 
    if ((da.a[3] & 0x7FF0) == 0x7FF0) { 
     /* value is either Inf or NaN */ 
     if ((da.a[0] == 0x0000) && 
      (da.a[1] == 0x0000) && 
      (da.a[2] == 0x0000) && 
      ((da.a[3] & 0x000F) == 0x0000)) { 
      /* significand is 0, so value is Inf */ 
      /* value becomes signed maximum real, */ 
      /* and error code prInf is returned */ 
      (*r)[1] = (*r)[0] = 0xFFFF; 
      (*r)[2] = 0x7FFF | 
         (da.a[3] & 0x8000); /* retain sign bit */ 
      return prInf; 
     } else { 
      /* significand is not 0, so value is NaN */ 
      /* value becomes 0.0, and prNaN code is returned */ 
      /* sign bit is ignored (no negative NaN) */ 
      (*r)[2] = (*r)[1] = (*r)[0] = 0x0000; 
      /* sign bit is ignored (-NaN -> +NaN) */ 
      return prNaN; 
     } 
    } 

    /* round significand if necessary */ 
    if ((da.a[0] & 0x1000) == 0x1000) { 
     /* significand's 40th bit set, so round significand up */ 
     if ((da.a[0] & 0xE000) != 0xE000) 
      /* room to increment 3 most significant bits */ 
      da.a[0] += 0x2000; 
     else { 
      /* carry bit to next element */ 
      da.a[0] = 0x0000; 
      /* carry from 0th to 1st element */ 
      if (da.a[1] != 0xFFFF) 
       da.a[1]++; 
      else { 
       da.a[1] = 0x0000; 
       /* carry from 1st to 2nd element */ 
       if (da.a[2] != 0xFFFF) 
        da.a[2]++; 
       else { 
        da.a[2] = 0x0000; 
        /* carry from 2nd to 3rd element */ 
        /* significand may overflow into exponent */ 
        /* exponent not full, so won't overflow */ 
        da.a[3]++; 
       } 
      } 
     } 
    } 

    /* get exponent for underflow/overflow tests */ 
    x = (da.a[3] & 0x7FF0) >> 4; 

    /* test for underflow */ 
    if (x < 895) { 
     /* value is below real range */ 
     (*r)[2] = (*r)[1] = (*r)[0] = 0x0000; 
     if ((da.a[3] & 0x8000) == 0x8000) 
      /* sign bit was set, so value was negative */ 
      return prNegUnderflow; 
     else 
      /* sign bit was not set */ 
      return prPosUnderflow; 
    } 

    /* test for overflow */ 
    if (x > 1149) { 
     /* value is above real range */ 
     (*r)[1] = (*r)[0] = 0xFFFF; 
     (*r)[2] = 0x7FFF | (da.a[3] & 0x8000); /* retain sign bit */ 
     return prOverflow; 
    } 

    /* value is within real range */ 
    (*r)[0] = (x - 894) | /* re-bias exponent */ 
       ((da.a[0] & 0xE000) >> 5) | /* begin significand */ 
       (da.a[1] << 11); 
    (*r)[1] = (da.a[1] >> 5) | 
       (da.a[2] << 11); 
    (*r)[2] = (da.a[2] >> 5) | 
       ((da.a[3] & 0x000F) << 11) | 
       (da.a[3] & 0x8000); /* copy sign bit */ 
    return prOK; 

} 
+0

@Seidr Je ne comprends pas le code que vous avez posté, où est-il obtenu les valeurs de retour et comment peupler d.a avec des valeurs au début? Merci – lancscoder

+0

D'après ce que je peux voir - la variable 'da' est un tableau de valeurs (bits de mémoire je suppose) dérivé de l'entrée double. Pour le deuxième argument de cette fonction, vous fournissez un POINTER à une variable 'réelle' que vous avez définie avant d'appeler cette fonction. La sortie est ensuite envoyée directement dans cette variable. Comme je l'ai dit, il s'agit d'une fonction C, et il faudra donc la modifier pour qu'elle fonctionne en C#. Je l'ai fourni purement comme un point dans la bonne direction. Je ne suis pas sûr de la prise en charge des pointeurs par C#, vous voyez. Bonne chance – Seidr

1

La façon la plus simple, si possible, serait de le convertir en une chaîne, passer, puis reconvertir en un Real48

5

Je suis tombé sur ce fil à la recherche de le même code. Voici ce que je fini par écrire:

public static byte [] Double2Real48(double d) 
{ 
    byte [] r48 = new byte[6]; 
    byte [] da = BitConverter.GetBytes(d); 

    for (int i = 0; i < r48.Length; i++) 
     r48[i] = 0; 

    //Copy the negative flag 
    r48[5] |= (byte)(da[7] & 0x80); 

    //Get the expoent 
    byte b1 = (byte)(da[7] & 0x7f); 
    ushort n = (ushort)(b1 << 4); 
    byte b2 = (byte)(da[6] & 0xf0); 
    b2 >>= 4; 
    n |= b2; 

    if (n == 0) 
     return r48; 

    byte ex = (byte)(n - 1023); 
    r48[0] = (byte)(ex + 129); 

    //Copy the Mantissa 
    r48[5] |= (byte)((da[6] & 0x0f) << 3);//Get the last four bits 
    r48[5] |= (byte)((da[5] & 0xe0) >> 5);//Get the first three bits 

    r48[4] = (byte)((da[5] & 0x1f) << 3);//Get the last 5 bits 
    r48[4] |= (byte)((da[4] & 0xe0) >> 5);//Get the first three bits 

    r48[3] = (byte)((da[4] & 0x1f) << 3);//Get the last 5 bits 
    r48[3] |= (byte)((da[3] & 0xe0) >> 5);//Get the first three bits 

    r48[2] = (byte)((da[3] & 0x1f) << 3);//Get the last 5 bits 
    r48[2] |= (byte)((da[2] & 0xe0) >> 5);//Get the first three bits 

    r48[1] = (byte)((da[2] & 0x1f) << 3);//Get the last 5 bits 
    r48[1] |= (byte)((da[1] & 0xe0) >> 5);//Get the first three bits 

    return r48; 

} 

Real48 est similaire à IEEE 754 en ce que le Mantisse sera le même. Le décalage de bits est nécessaire pour obtenir la Mantisse au bon endroit.

exposant Real48 a une polarisation de 129 et la double présente une polarisation de 1023.

Le drapeau négatif est stocké dans le premier bit du dernier octet.

Remarques: Je ne pense pas que ce code fonctionnera sur une grosse machine d'extrémité. Il ne vérifie pas NAN ou INF.

Voici le code qui convertit un real48 en un double. Il a été porté par le compilateur Free Pascal:

static double real2double(byte [] r) 
{ 
    byte [] res = new byte[8]; 
    int exponent; 

    //Return zero if the exponent is zero   
    if (r[0] == 0) 
     return (double)0; 

    //Copy Mantissa 
    res[0] = 0; 
    res[1] = (byte)(r[1] << 5); 
    res[2] = (byte)((r[1] >> 3) | (r[2] << 5)); 
    res[3] = (byte)((r[2] >> 3) | (r[3] << 5)); 
    res[4] = (byte)((r[3] >> 3) | (r[4] << 5)); 
    res[5] = (byte)((r[4] >> 3) | ((r[5] & 0x7f) << 5)); 
    res[6] = (byte)((r[5] & 0x7f) >> 3); 

    //Copy exponent 
    //correct exponent 
    exponent = (r[0] + (1023-129)); 
    res[6] = (byte)(res[6] | ((exponent & 0xf) << 4)); 
    res[7] = (byte)(exponent >> 4); 

    //Set Sign 
    res[7] = (byte)(res[7] | (r[5] & 0x80)); 
    return BitConverter.ToDouble(res, 0); 
} 
0
double Double_Real48(double d) 
{ 
    unsigned long long r48 = 0, tmp; 

    tmp = *(long long *)&d;//m 
    tmp/=0x20; 
    tmp&=0x7FFFFFFFFF00; 
    r48+=tmp; 

    tmp = *(long long *)&d;//e 
    tmp/=0x10000000000000; 
    tmp-=894; 
    tmp&=0xFF; 
    if (tmp == 0) return 0.0; 
    r48+=tmp; 

    tmp = *(long long *)&d;//s 
    tmp/=0x10000; 
    tmp&=0x800000000000; 
    r48+=tmp; 

    return *(double *)&r48; 
} 

double Real48_Double(double r48) 
{ 
    unsigned long long d = 0, tmp; 

    tmp= *(long long *)&r48;//m 
    tmp&=0x7FFFFFFFFF00; 
    tmp*=0x20; 
    d+=tmp; 

    tmp= *(long long *)&r48;//e 
    tmp&=0xFF; 
    if (tmp == 0) return 0.0; 
    tmp+=894; 
    tmp*=0x10000000000000; 
    d+=tmp; 

    tmp= *(long long *)&r48;//s 
    tmp&=0x800000000000; 
    tmp*=0x10000; 
    d+=tmp; 

    return *(double *)&d; 
} 
+0

Depuis lors, stdcall dll habituellement real48 6 octets est traité comme 8 octets. En conséquence, nous ne pouvons même pas envoyer un type de conversion Long Long. changement approprié appelé prototype de fonction. –

+0

'unsigned long long Double_Real48 (double d) {unsigned long long r48 = 0, ...... return r48; } ' –

0

en C/C++

typedef struct { 
    unsigned char exponent; // 8 bites; 
    unsigned long mantisaLo; // 32 of 39 bites 
    unsigned char mantisaHi : 7, sign : 1; // 7 of 39 bites 
} T_Real48; 

typedef struct { 
    unsigned long mantisaLo; // 32 of 52 bites 
    unsigned long mantisaHi:20, exponent: 11, sign : 1; // 20 of 52 bites 
} T_Double64; 

double doubleToReal48(double val) 
{ 
    T_Real48 real48; 
    T_Double64 *double64 = (T_Double64*) &val; 

    real48.mantisaHi = double64->mantisaHi >> 13; 
    real48.mantisaLo =(double64->mantisaLo >> 13) + ((double64->mantisaHi & 0x1FFF) << 19); 
    real48.exponent = double64->exponent - 894; 
    real48.sign  = double64->sign; 

    if (real48.exponent == 0) { 
     real48.mantisaHi = 0; 
     real48.mantisaLo = 0; 
    } 

    return *(double *)&real48; 
} 

double real48ToDouble(double val) 
{ 
    T_Real48 *real48 = (T_Real48*) &val; 
    T_Double64 double64; 

    double64.mantisaHi = (real48->mantisaHi << 13) + (real48->mantisaLo >> 19); 
    double64.mantisaLo = real48->mantisaLo << 13; 
    double64.exponent = real48->exponent + 894; 
    double64.sign  = real48->sign; 

    return *(double *)&double64; 
}