Partage
  • Partager sur Facebook
  • Partager sur Twitter

La LibC des Zéros

Objectif : recoder la libC.

5 août 2012 à 2:17:55

D'actualité ou pas, le fait est que ça existe et que le C a été conçu pour être implémentable sur ce genre d'architecture. Après, si tu t'en fous et que tu veux seulement écrire du C pour telle ou telle machine, libre à toi bien sûr.
  • Partager sur Facebook
  • Partager sur Twitter
5 août 2012 à 3:08:20

Ce que tu dis est discutable hein, dans un domaine différent, le web, il serait débile de toujours faire attention à IE6 par exemple donc... c'est discutable non?

Mais si il n'y a aucun cas ou même sur ces vieux trucs les codes pourraient planter, alors il n'y a aucun soucis.
  • Partager sur Facebook
  • Partager sur Twitter
5 août 2012 à 4:12:10

Citation : Mr21


Ce que dit le gars qui a reply ne semble plus a jour si?



A jour ou pas le c garantit que si deux pointeurs pointent sur le même objet alors ils sont égaux (dans le sens ou p==q retourne 1).
ce qui se passe dans des couches plus basses ne concerne pas le c comme tu peux l'imaginer. (je dis ca en pensant aux espaces d'adressage virtuels).

Citation : c99

Two pointers compare equal if and only if both are null pointers, both are pointers to the same object (including a pointer to an object and a subobject at its beginning) or function, [...]



maintenant si, effectivement, ils ont la même valeur ou pas, c'est une autre histoire, et tu as bien sur le droit de t’intéresser aux couches plus basses.

A force de citer la norme je deviens lourd :-° mais je trouve qu'elle répond a toutes tes questions.
  • Partager sur Facebook
  • Partager sur Twitter
5 août 2012 à 11:03:18

Citation : Mr21

Ce que tu dis est discutable hein, dans un domaine différent, le web, il serait débile de toujours faire attention à IE6 par exemple donc... c'est discutable non?



Euh, j'ai pas émis de jugement de valeur ; j'ai dit : ça dépend de ce que tu fais, si c'est pas important pour toi, bah c'est pas important pour toi, c'est tout. Comme tu as le choix de prendre en charge IE6 ou pas, tu as le choix de te limiter à la norme ou pas.

Citation

Mais si il n'y a aucun cas ou même sur ces vieux trucs les codes pourraient planter, alors il n'y a aucun soucis.



Bah on t'en a donné un d'exemple où ça ne marcherait pas : si la mémoire est segmentée. Après, j'ai jamais fait de C pour ce genre de plateforme donc faut pas compter sur moi pour te donner un vrai exemple.

'Fin voilà la norme c'est la norme ; faut prendre ça pour ce que c'est. La norme ne te dit pas comment penser, t'sais, après t'es libre de faire ce que tu veux, mais fais le en connaissance de cause.
  • Partager sur Facebook
  • Partager sur Twitter
5 août 2012 à 14:44:54

Citation : Mr21

Ce que dit le gars qui a reply ne semble plus a jour si?
Il mentionne des pointeurs différents qui pointeraient sur la même chose.

"8086" si il parle de ça :
http://fr.wikipedia.org/wiki/Intel_8086
Est-ce encore d'actualité?



Il parle bien de ce processeur. En fait, il n'est sans doute aujourd'hui plus utilisé sur aucune machine (sauf ordinosaure). Cependant, il faut savoir que les processeurs d'architectures x86 et x86_64 disposent toujours de ce mode de fonctionnement lors de leur lancement. C'est ce que l'on appel le mode réel ou mode 16 bits.

Dans ce mode, il est possible d'adresser 1 Mo de mémoire à l'aide d'un couple d'adresses : celle du segment et celle du décalage (au sein du segment). Les segments ont une taille de 64 Ko et commencent tous les 16 octets. De ce fait, il est possible de référencer un même objet à l'aide de couples segment:décalage différents.

Ainsi, prends par exemple un tableau de quatre char situé dans le segments 0 à un décalage de 16 (0:16). Ce même tableau peut-être référencé dans le segment suivant à l'adresse 1:0 (puisque les segments sont séparés de 16 octets). Imagine maintenant que tu as deux pointeurs référençant chacun ce tableau mais dans l'un et l'autre segment. L'un aura une valeur de 16, l'autre de 0 alors qu'ils référencent le même objet.

Bon, je précise que je ne connais pas plus que cela le mode réel, je m'excuse donc par avance si j'ai dit une grosse bêtise. Je pense cependant avoir préciser l'idée et le problème qui peut survenir en cas de comparaison de pointeurs.
  • Partager sur Facebook
  • Partager sur Twitter
5 août 2012 à 21:59:40

Très intéressant tout ça :p On s'éloigne un peu du sujet original mais très intéressant ^^

Sinon, Taurre, je n'ai toujours pas trouvé un moyen (acceptable niveau complexité et temps d'exécution) pour l'affichage des flottants :(

Sinon au niveau de %p, perso je l'ai implémenté tel que "%#08X" (version MinGW, en rajoutant les options possibles). Après, ce n'est peut être pas normale, la norme définie telle la conversion void* -> int ? Au passage, avez-vous un lien vers la norme C ? Sur internet je ne trouve que des livres à acheter, je ne sais pas si elle est consultable en ligne, et j'aimerais bien voir quelques petits trucs !
  • Partager sur Facebook
  • Partager sur Twitter
5 août 2012 à 22:17:05

Citation : Holt

Au passage, avez-vous un lien vers la norme C ? Sur internet je ne trouve que des livres à acheter, je ne sais pas si elle est consultable en ligne, et j'aimerais bien voir quelques petits trucs !



Un draft : http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf

Sinon le texte officiel est payant normalement.
  • Partager sur Facebook
  • Partager sur Twitter
5 août 2012 à 22:25:26

Merci beaucoup :)

Du coup, je m'auto répond en citant la norme :p

Citation : C99

Any pointer type may be converted to an integer type. Except as previously specified, the result is implementation-defined. If the result cannot be represented in the integer type, the behavior is undefined. The result need not be in the range of values of any integer type.

  • Partager sur Facebook
  • Partager sur Twitter
5 août 2012 à 22:31:42

Citation : Holt

Sinon, Taurre, je n'ai toujours pas trouvé un moyen (acceptable niveau complexité et temps d'exécution) pour l'affichage des flottants :(



Ben, pour être honnête, moi non plus :-°
La seule solution que je vois sans s'embarquer dans des codes comme ceux de la glibc ou de celle des BSD, c'est d'effectuer les calculs à l'aide de tableau de char. Mais cela risque d'être extrêmement lourd...

Citation : Holt

Au passage, avez-vous un lien vers la norme C ? Sur internet je ne trouve que des livres à acheter, je ne sais pas si elle est consultable en ligne, et j'aimerais bien voir quelques petits trucs !



Au niveau des brouillons, il existe les trois suivants pour les normes C89, C99 et C11 (j'ai pris les plus récents) :

- C89 (X3J11/88-090) ;
- C99 (n1256) ;
- C11 (n1570).

@uknow : pour la norme C99, le dernier en date est le n1256 ;)
  • Partager sur Facebook
  • Partager sur Twitter
5 août 2012 à 22:36:25

Citation : Taurre


@uknow : pour la norme C99, le dernier en date est le n1256 ;)



Exact, au temps pour moi.
  • Partager sur Facebook
  • Partager sur Twitter
5 août 2012 à 22:51:01

Citation : Taurre

Citation : Holt

Sinon, Taurre, je n'ai toujours pas trouvé un moyen (acceptable niveau complexité et temps d'exécution) pour l'affichage des flottants :(



Ben, pour être honnête, moi non plus :-°
La seule solution que je vois sans s'embarquer dans des codes comme ceux de la glibc ou de celle des BSD, c'est d'effectuer les calculs à l'aide de tableau de char. Mais cela risque d'être extrêmement lourd...


C'est ce que j'avais pensé aussi en fait (ne marche que pour la norme > C99, avec une implémentation des flottants en IEE 754) :
  • Stockage de la mantisse dans un unsigned long long, puis conversion en chaîne de caractère
  • Stockage de l'exposant dans un int (ou short)
  • Calcul directement sur la chaîne de caractère en multipliant par 2 autant de fois qu'il faut

Ca revient limite à codé une gestion des très grands nombres en se limitant à la multiplication par 2, donc bon niveau temps de calcul c'est pas très correct !
Je ne vois pas d'autres moyen pour garantir que le nombre est exact que de passer par la mantisse... La multiplication par 10, ou division par 10 (et je ne parle même pas du fmod... ) sont trop "approximative" pour obtenir un résultat correct.
En écrivant ce texte (et bah oui je réfléchis en même temps), et via mes tests passé, une idée me vient à l'esprit (c'est poétique tout ça) ! La division par 10 détruit petit à petit le nombre, grossièrement, en faisant ceci :
for () {
    fmod(val, 10) ;
    val /= 10 ;
}

On n'obtient 15 décimal correctes, ce qui est normal étant donnée la représentation du nombre en mémoire (cf. un de mes posts et ceux de Taurre à propos de DBL_MAX_DIG).
En revanche ! On obtient des résultats très correct pour les dernières décimales avec ceci :
double div = 10.0 ;
for () {
    fmod(val, div) * 10.0 / 10.0 ; 
    div *= 10.0 ;
}

Pourquoi ne pas tester ceci (je ne peux pas le faire pour le moment, si quelqu'un se sent l'âme !) :
double cval = val; // copie de val
double div ;
int i ;
for (i=0, div=10.0 ; i < ... ; ++i, div*=10, cval /= 10.0) {
    if (i > DBL_DIG_MAX) {
        fmod(cval, 10.0) ;
    }
    else {
        fmod(val,div) * 10.0 / div ;
    }
}
  • Partager sur Facebook
  • Partager sur Twitter
5 août 2012 à 23:13:00

Citation : Holt

C'est ce que j'avais pensé aussi en fait (ne marche que pour la norme > C99, avec une implémentation des flottants en IEE 754)



En fait, il est possible de récupérer la mantisse de manière portable en utilisant les constantes de l'en-tête <float.h>. En effet, en divisant le nombre par la base de sa mantisse (FLT_RADIX) jusqu'à atteindre l'exposant zéro, il est possible de reconstituer la mantisse en récupérant successivement la partie entière de ce nombre multiplié par FLT_RADIX.

Dit plus simplement, en code, cela donne :

#include <stdio.h>
#include <float.h>
#include <math.h>
	

static char *
mant(double n)
{
	static char ret[DBL_MANT_DIG + 1];
	unsigned i;

	while (n >= 1.)
		n /= FLT_RADIX;

	for (i = 0; i < DBL_MANT_DIG; ++i) {
		n *= FLT_RADIX;
		ret[i] = (int)n + '0';
		n -= floor(n);
	}

	ret[i] = '\0';
	return ret;
}


int
main(void)
{
	double n = 3.;

	puts(mant(n));
	return 0;
}


  • Partager sur Facebook
  • Partager sur Twitter
5 août 2012 à 23:16:58

Effectivement j'avais oublié ce petit RADIX :p C'est très intéressant d'ailleurs niveau portabilité, je vais le garder sous le coude :p
  • Partager sur Facebook
  • Partager sur Twitter
Anonyme
21 novembre 2012 à 19:56:37

Je me permet de remonter le sujet en ajoutant deux fonctions de math.h : exp et sqrt. En même temps, ça peut permettre à d'autre de voir le sujet. Faut pas qu'il tombe dans l'oubli, c'est un projet très intéressant et instructif.

double my_sqrt(double x)
{
    double res = 1;
    int i;
    const int prec = 20;

    for (i = 0; i < prec; ++i)
    {
        res = (res + x / res) / 2;
    }

    return res;
}


double my_exp(double x)
{
    double res = 1 + x;
    double puis = x, fact = 1;
    const int pres = 25;
    int i;

/* Comme res vaut déjà 1 + x, il faut commencer à x^2, soit i = 2 */
    for(i = 2; i < pres; i++)
    {
        puis *= x;
        fact *= i;
        res += puis / fact;
    }
    return res;
}

#define EXP(x) printf("%.10f =?= %.10f\n", my_exp(x), exp(x))
#define SQRT(x) printf("%.4f =?= %.4f\n", my_sqrt(x), sqrt(x))

int main(void)
{
    SQRT(3);
    SQRT(2);
    SQRT(100);
    EXP(1);
    EXP(0);
    EXP(6);
    return 0;
}



Et une autre astuce que je viens de découvrir (elle marche, j'ai du mal à croire que ça ne plante pas) pour recoder facilement vsprintf et sprintf :

int my_vsprintf(char * buff, const char * str, va_list args)
{
    int count = my_vfprintf((FILE*)buff, str, args);
    return count;
}

int my_sprintf(char * buff, const char * str, ...)
{
    int count;
    va_list ap;

    va_start(ap, str);
    count = vsprintf(buff, str, ap);
    va_end(ap);

    return count;
}
  • Partager sur Facebook
  • Partager sur Twitter
21 novembre 2012 à 20:51:24

L'astuce de vsprintf, c'est assez dégueulasse. Ça dépend de ton implémentation de FILE, et il y a plusieurs problèmes (SA entré autrs)
  • Partager sur Facebook
  • Partager sur Twitter
Staff désormais retraité.
Anonyme
21 novembre 2012 à 21:12:37

Voilà stdarg sans les built-in (mais non portable, comme préciser par Taurre plus bas):

#ifndef MY_STDARG_H
#define MY_STDARG_H

#define my_va_size(type) (((sizeof(type) + sizeof(long) - 1) / sizeof(long)) * sizeof(long))

typedef char* my_va_list;

#define my_va_start(ap, arg) ((ap) = ((my_va_list)(&arg) + my_va_size(arg)))

#define my_va_end(ap) ((void)0)

#define my_va_arg(ap, type) ((ap) += my_va_size(type), *((type*)((ap) - my_va_size(type))))

#endif



math (fabs, ceil, floor) :


double my_fabs(double x)
{
    return x >= 0.0 ? x : -x;
}

double my_ceil(double x)
{
    return (double)((int)x + 1);
}

double my_floor(double x)
{
    return (double)((int)x);
}


et stdlib (abs, labs, llabs, atoi, atol, atoll)


int my_abs(int x)
{
    return x >= 0 ? x : -x;
}

long int my_labs(long int x)
{
    return x >= 0L ? x : -x;
}

long long int my_llabs(long long int x)
{
    return x >= 0LL ? x : -x;
}

int my_atoi(const char* str)
{
    return (int)my_atol(str);
}

long int my_atol(const char* str)
{
    return (long int)my_atoll(str);
}

long long int my_atoll(const char* str)
{
    long long int nb = 0;
    int signe;

    while((*str == ' ') || (*str >= '\t' && *str <= '\r'))
    {
        str++;
    }

    signe = (*str == '-') ? -1 : 1;

    if(*str == '+' || *str == '-')
    {
        str++;
    }

    while(*str >= '0' && *str <= '9')
    {
        nb *= 10;
        nb += *str++ - '0';
    }

    return signe * nb;
}


Blodangan
  • Partager sur Facebook
  • Partager sur Twitter
Anonyme
21 novembre 2012 à 21:15:26

Oui certes c'est à la limite de l'obfuscation, mais ça marche (j'ai testé sous Windows avec MinGw et Visual C++ 2010). De plus, pour recoder ces deux fonctions, il faut obligatoirement passer par la manipulation de FILE, qui change en fonction de la LibC utilisée.

Par contre, quels sont les problèmes dont tu parle ?

@Blodangan : j'ai l'impression que my_va_size se répète :

(((sizeof(type) + sizeof(long) - 1) / sizeof(long)) * sizeof(long))


est équivalent à

(sizeof(type) + sizeof(long) - 1)
  • Partager sur Facebook
  • Partager sur Twitter
21 novembre 2012 à 21:19:55

@Blodangan: ton implémentation de l'en-tête <stdarg.h> suppose que les arguments sont passés via la pile (ce qui n'est pas le cas avec les ABI x86_64 et x32), qu'ils soient empliés en ordre inverse et que les données soient alignées sur la taille d'un long peut importe leur type (et encore, si le type double et/ou long double est plus grand qu'un long, tu seras refait) edit: non, en fait je ne comprends pas ton calcul. :-°
  • Partager sur Facebook
  • Partager sur Twitter
Anonyme
21 novembre 2012 à 21:25:50

Informaticienzero : Non, c'est normal, pour sizeof(type) = 8 et sizeof(long) = 4 on a :

((8 + 4 - 1) / 4 * 4
= 11 / 4 * 4 = 2 * 4
= 8

et pour toi cela fait :

8 + 4 - 1
= 11

Taurre : Effectivement tu as raison, j'edit pour préciser que ce n'est pas portable.

Blodangan
  • Partager sur Facebook
  • Partager sur Twitter
Anonyme
21 novembre 2012 à 21:28:04

Citation : Blodangan

Informaticienzero : Non, c'est normal, pour sizeof(type) = 8 et sizeof(long) = 4 on a :

((8 + 4 - 1) / 4 * 4
= 11 / 4 * 4 = 2 * 4
= 8

et pour toi cela fait :

8 + 4 - 1
= 11



Ah mais oui je suis bête, ce sont des entiers. A force d'utiliser tout le temps les réels en maths, je ne sais même plus utiliser les entiers. :-°
  • Partager sur Facebook
  • Partager sur Twitter
21 novembre 2012 à 21:58:49

@informaticienzero : Il n'aurait pas été plus logique de coder pow() avant sqrt() ?
  • Partager sur Facebook
  • Partager sur Twitter
Tell me and I forget. Teach me and I remember. Involve me and I learn.
Anonyme
21 novembre 2012 à 22:03:41

pow de la libC est assez complexe car il gère les nombres décimaux.

Blodangan
  • Partager sur Facebook
  • Partager sur Twitter
21 novembre 2012 à 22:05:17

Citation : Blodangan

pow de la libC est assez complexe car il gère les nombres décimaux.



sqrt(x) -> pow( x , 0.5 ) ;)
  • Partager sur Facebook
  • Partager sur Twitter
21 novembre 2012 à 22:07:57

Citation : Blodangan

pow de la libC est assez complexe car il gère les nombres décimaux.



On peut commencer par une version naïve, en exploitant que <math>\(a^b = exp(b \times \log(a))\)</math>. Pour l'exponentielle et le logarithme, il y a des formules d'approximation qui traînent sur le Web (pour faire naïf, on peut prendre un développement limité). Ça donne déjà des approximations raisonnables, un scientifique ayant besoin de haute précision utilisera de toute façon une bibliothèque spécialisée. :)
  • Partager sur Facebook
  • Partager sur Twitter
J'ai déménagé sur Zeste de savoir — Ex-manager des modérateurs.
Anonyme
21 novembre 2012 à 22:09:01

Citation : NDKa

@informaticienzero : Il n'aurait pas été plus logique de coder pow() avant sqrt() ?



Pourquoi ça ? J'ai préféré utiliser la méthode de Héron, elle me semble moins lourde que de recoder pow.

Sinon, est-ce que vous pouvez tester le code ci-dessous et dire s'il marche bien chez vous ?

#include <stdio.h>
#include <stdarg.h>

int my_vsprintf(char * buff, const char * str, va_list args)
{
    int count = vfprintf((FILE*)buff, str, args);
    return count;
}

int my_sprintf(char * buff, const char * str, ...)
{
    int count;
    va_list ap;

    va_start(ap, str);
    count = vsprintf(buff, str, ap);
    va_end(ap);

    return count;
}

int main(void)
{
    char t1[BUFSIZ], t2[BUFSIZ];
    int a = my_sprintf(t1, "%+7.4d", 100);
    int b = sprintf(t2, "%+7.4d", 100);

    printf("t1 = %s t2 = %s", t1, t2);
    printf("\n%d =?= %d\n", a, b);

    return 0;
}
  • Partager sur Facebook
  • Partager sur Twitter
21 novembre 2012 à 22:37:34

Citation : informaticienzero

Pourquoi ça ? J'ai préféré utiliser la méthode de Héron, elle me semble moins lourde que de recoder pow.


Oui pow() n'est pas légère, mais de toutes façons il faut bien y passer un moment ou à un autre. Ensuite, et bien, see uknow.
Et pour le coup on sera d'accord sur qu'elle façon de faire est la plus propre/légère pour sqrt().
  • Partager sur Facebook
  • Partager sur Twitter
Tell me and I forget. Teach me and I remember. Involve me and I learn.
Anonyme
21 novembre 2012 à 22:46:47

J'avais effectivement oublié cette astuce (<math>\(\sqrt[y]{x} = x^\frac{1}{y}\)</math>). Sinon tu peux le dire si mon code marche chez toi ?
  • Partager sur Facebook
  • Partager sur Twitter
21 novembre 2012 à 23:58:03

Hey !
Je voulais vous montrer la fonction srand et rand, c'est pas le plus beau code mais voilà x)

static unsigned int _holdran;

void    my_srand(unsigned int seed)
{
    _holdrand = (long)seed;
}

int     my_rand(void)
{
    return(((_holdrand = _holdrand * 214013L + 2531011L) >> 16) & 0x7fff);
}
  • Partager sur Facebook
  • Partager sur Twitter
22 novembre 2012 à 10:34:16

inf0 je te répondrai après, pas facile d'expliquer sur Smartphone.
Lebug63 : tu pourrais expliquer ton implémentation ? Avec ton cast étrange, on dirait un code réaménagé et légèrement retouché.
  • Partager sur Facebook
  • Partager sur Twitter
Staff désormais retraité.
22 novembre 2012 à 10:41:54

Citation : informaticienzero

Sinon, est-ce que vous pouvez tester le code ci-dessous et dire s'il marche bien chez vous ?



Oui, il fonctionne très bien, mais c'est assez logique puisque tu fais appel à vsprintf et non à my_vsprintf (ligne 16). ^^
  • Partager sur Facebook
  • Partager sur Twitter