IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Lazarus Pascal Discussion :

Bug sur la fonction IncDay ? [Lazarus]


Sujet :

Lazarus Pascal

  1. #1
    Membre averti
    Profil pro
    Inscrit en
    Septembre 2005
    Messages
    299
    Détails du profil
    Informations personnelles :
    Âge : 55
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations forums :
    Inscription : Septembre 2005
    Messages : 299
    Points : 330
    Points
    330
    Par défaut Bug sur la fonction IncDay ?
    Bonjour (et bonnes fêtes de noël !)

    Je suis en train de mettre au point une fonction de convertisseur entre le calendrier républicain et grégorien.
    Pour cela, je détermine le nombre de jours écoulés depuis le premier jour de ce calendrier (22/9/1793).
    Après avoir observé un écart par rapport aux valeurs attendues, je constate que :
    - DateToStr(IncDay(EncodeDate(1793,9,22),38817)) renvoie le 02/01/1900 (pour info, c'est le 12 nivose 108)
    - DateToStr(IncDay(EncodeDate(1793,9,22),38818)) renvoie le 04/01/1900 (alors que le nombre de jour à incrémenter n'a augmenté que d'un)

    Ma ou mes questions sont donc les suivantes :
    - Le 3 janvier 1900 a t-il vraiment existé ou un discontinum de l'espace temps est-il intervenu à ce moment là ? (les témoins manquent pour me confirmer cette assertion)
    - A moins qu'il n'y ait un trou dans la raquette sur la fonction IncDay de FreePascal ?
    (je précise que si on n'emploie pas cette fonction et qu'on code avec DateToStr(EncodeDate(1793,9,22)+38818)), on trouve bien le 03/01/1900)

    Merci par avance pour vos lumières sur ce bug avant-gardiste de l'an 1900...

  2. #2
    Responsable Pascal, Lazarus et Assembleur


    Avatar de Alcatîz
    Homme Profil pro
    Ressources humaines
    Inscrit en
    Mars 2003
    Messages
    7 963
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 57
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ressources humaines
    Secteur : Service public

    Informations forums :
    Inscription : Mars 2003
    Messages : 7 963
    Points : 59 639
    Points
    59 639
    Billets dans le blog
    2
    Par défaut
    Bonjour,

    Étant donné que le type TDateTime est en fait un Double, il y a manifestement un arrondi qui entraîne l'erreur.

    Voici l'implémentation de IncDay dans l'unité DateUtils :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    const
      TDateTimeEpsilon = 2.2204460493e-16;
     
    Procedure MaybeSkipTimeWarp(OldDate: TDateTime; var NewDate: TDateTime);
    begin
      if (OldDate>=0) and (NewDate<-TDateTimeEpsilon) then
        NewDate:=int(NewDate-1.0+TDateTimeEpsilon)-frac(1.0+frac(NewDate))
      else if (OldDate<=-1.0) and (NewDate>-1.0+TDateTimeEpsilon) then
        NewDate:=int(NewDate+1.0-TDateTimeEpsilon)+frac(1.0-abs(frac(1.0+NewDate)));
    end;
     
    Function IncDay(const AValue: TDateTime; const ANumberOfDays: Integer): TDateTime;
    begin
      Result:=AValue+ANumberOfDays;
      MaybeSkipTimeWarp(AValue,Result);
    end;
    Ceci dit, je ne reproduis pas ton erreur :
    38811 : 27-12-1899
    38812 : 28-12-1899
    38813 : 29-12-1899
    38814 : 30-12-1899
    38815 : 31-12-1899
    38816 : 01-01-1900
    38817 : 02-01-1900
    38818 : 03-01-1900
    38819 : 04-01-1900
    38820 : 05-01-1900
    38821 : 06-01-1900
    38822 : 07-01-1900

  3. #3
    Membre averti
    Profil pro
    Inscrit en
    Septembre 2005
    Messages
    299
    Détails du profil
    Informations personnelles :
    Âge : 55
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations forums :
    Inscription : Septembre 2005
    Messages : 299
    Points : 330
    Points
    330
    Par défaut
    Merci pour ta réponse.
    Je précise mes versions utilisées : 3.0 RC1 pour Lazarus et 3.2.2 pour FPC.
    J'ai bien le même code que toi pour les 2 fonctions.
    Au final, le décalage ne semble pas lié à un problème d'arrondi puisqu'il continue les jours suivants :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    var iJour : integer;
    Begin
      memo2.Lines.Clear;
      for iJour := 38811 to 38822 do
        memo2.Lines.add(inttostr(iJour) + ' : ' + DateToStr(IncDay(EncodeDate(1793,9,22),iJour)));
    Donne :
    Nom : bug IncDay.JPG
Affichages : 123
Taille : 16,8 Ko

    Je vous promets que je n'ai abusé de rien pour les fêtes de noël mais si une personne obtient le même résultat, cela me rassurerait quelque peu même si ça ne résoudra pas le pb
    Du coup, j'attends un peu avant de créer un signalement de bug.

  4. #4
    Modérateur
    Avatar de tourlourou
    Homme Profil pro
    Biologiste ; Progr(amateur)
    Inscrit en
    Mars 2005
    Messages
    3 877
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Biologiste ; Progr(amateur)

    Informations forums :
    Inscription : Mars 2005
    Messages : 3 877
    Points : 11 373
    Points
    11 373
    Billets dans le blog
    6
    Par défaut
    Bonjour,
    Je n'ai pas abusé plus que toi et obtiens le même résultat !
    CodeTyphon 6.90 avec FreePascal 3.3.1 et cible Windows 64

  5. #5
    Responsable Pascal, Lazarus et Assembleur


    Avatar de Alcatîz
    Homme Profil pro
    Ressources humaines
    Inscrit en
    Mars 2003
    Messages
    7 963
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 57
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ressources humaines
    Secteur : Service public

    Informations forums :
    Inscription : Mars 2003
    Messages : 7 963
    Points : 59 639
    Points
    59 639
    Billets dans le blog
    2
    Par défaut
    J'ai fait d'autres essais :

    • Lazarus 3.0 sous Windows 10 64 : date manquante ;
    • Lazarus 3.0 sous Garuda Linux 64 : aucun problème.

    Les deux OS sur la même machine (pas en virtualisation, en dual boot).



    [EDIT]
    Je suis allé jusqu'à l'an 2888, soit 400.000 itérations : l'erreur ne survient qu'au jour 38818 (et affecte tous les jours suivants).
    Ceci dit, les chances pour que l'on découvre ce bug étaient minces : on calcule rarement une date à partir de l'an 1793.
    [/EDIT]

  6. #6
    Modérateur
    Avatar de tourlourou
    Homme Profil pro
    Biologiste ; Progr(amateur)
    Inscrit en
    Mars 2005
    Messages
    3 877
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Biologiste ; Progr(amateur)

    Informations forums :
    Inscription : Mars 2005
    Messages : 3 877
    Points : 11 373
    Points
    11 373
    Billets dans le blog
    6
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
      Memo1.Lines.Add(DaysBetween(-1.3, EncodeDate(1793,9,22)).ToString);
      Memo1.Lines.Add(DaysBetween(-0.3, EncodeDate(1793,9,22)).ToString);
      Memo1.Lines.Add(DaysBetween(+0.3, EncodeDate(1793,9,22)).ToString);
      Memo1.Lines.Add(DaysBetween(+1.3, EncodeDate(1793,9,22)).ToString);
    donne
    38812
    38813
    38813
    38814
    ce qui s'explique ainsi :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    { ---------------------------------------------------------------------
        Period functions.
      ---------------------------------------------------------------------}
     
    {
      These functions are declared as approximate by Borland.
      A bit strange, since it can be calculated exactly ?
     
      -- No, because you need rounding or truncating (JM)
    }
     
     
    Function DateTimeDiff(const ANow, AThen: TDateTime): TDateTime;
    begin
      Result:= ANow - AThen;
      if (ANow>0) and (AThen<0) then
        Result:=Result-0.5
      else if (ANow<-1.0) and (AThen>-1.0) then
        Result:=Result+0.5;
    end;
    et par l'avertissement de la fonction MaybeSkipTimeWarp :
    TDateTime is not defined in the interval [-1.0..0.0[. Additionally, when
    negative the time part must be treated using its absolute value (0.25 always
    means "6 a.m.") -> skip the gap and convert the time part when crossing the
    gap -- and take care of rounding errors
    Reste à comprendre le décalage obtenu au passage :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
       Memo1.Lines.Add('Origine du TDateTime (30/12/1899) : ' + DateToStr(-0.8));
      Memo1.Lines.Add('Origine 22/09/1793 doit valoir -38814 : ' + Double(EncodeDate(1793,9,22)).ToString);
      Memo1.Lines.Add('Origine + 38817 doit valoir 3 : ' + (38817-38814).ToString);
      Memo1.Lines.Add('en passant par IncDay origine + 38817 vaut : ' + Double(IncDay(EncodeDate(1793,9,22),38817)).ToString);
      Memo1.Lines.Add('Origine + 38818 doit valoir 4 : ' + (38818-38814).ToString);
      Memo1.Lines.Add('en passant par IncDay origine + 38818 vaut : ' + Double(IncDay(EncodeDate(1793,9,22),38818)).ToString);
    qui fournit

    Origine du TDateTime (30/12/1899) : 30/12/1899
    Origine 22/09/1793 doit valoir -38814 : -38814
    Origine + 38817 doit valoir 3 : 3
    en passant par IncDay origine + 38817 vaut : 3
    Origine + 38818 doit valoir 4 : 4
    en passant par IncDay origine + 38818 vaut : 5
    [EDIT] Plus j'y comprends, moins j'y comprends... DateTimeDiff ne fonctionne pas pour les dates antérieures à l'origine...

  7. #7
    Membre averti
    Profil pro
    Inscrit en
    Septembre 2005
    Messages
    299
    Détails du profil
    Informations personnelles :
    Âge : 55
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations forums :
    Inscription : Septembre 2005
    Messages : 299
    Points : 330
    Points
    330
    Par défaut
    Ouf, je me sens moins seul... Je suis effectivement sous Windows10 64.
    Ce qui me gênait, c'est que
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    Function IncDay(const AValue: TDateTime; const ANumberOfDays: Integer): TDateTime;
    begin
      Result:=AValue+ANumberOfDays;
    dans Dateutils aboutit à cette erreur et que si on remplace la fonction IncDay par ce qu'elle fait, autrement dit une simple somme DateToStr(EncodeDate(1793,9,22)+iJour), ça fonctionne.
    Du coup, j'ai focalisé mon attention sur la fonction MaybeSkipTimeWarp que j'ai dupliqué en local et j'ai pu localiser la ligne fautive qui est dans le Else de cette fonction et qui est bien exécutée dans mon exemple : NewDate:=int(NewDate+1.0-TDateTimeEpsilon)+frac(1.0-abs(frac(1.0+NewDate)));
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
      dResult:=-38814+38817;  //AValue+ANumberOfDays;  =>dResult = 3
      MaybeSkipTimeWarp(-38814,dResult);
      showmessage(floattostr(dResult)); // Affichage : 3
     
      dResult:=-38814+38818;  //AValue+ANumberOfDays;  =>dResult = 4
      MaybeSkipTimeWarp(-38814,dResult);
      showmessage(floattostr(dResult)); //Affichage : 5
    Le problème semble donc être un problème de virgule flottante lié à l'utilisation de la fonction Frac... et au système d'exploitation !
    Les conséquences ne me semblent pas anodines car cette fonction est appelée dans toutes les fonction de type inc<...> (week, Hour, Minute, Second, Millisecond) et cela signifie que ces fonctions ne donnent pas toujours le résultat escompté dès lors qu'une date est antérieure au 01/01/1900.

    On fait quoi, maintenant ? Signalement auprès de l'équipe de dev ?

  8. #8
    Expert confirmé

    Homme Profil pro
    Directeur de projet
    Inscrit en
    Mai 2013
    Messages
    1 459
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Directeur de projet
    Secteur : Service public

    Informations forums :
    Inscription : Mai 2013
    Messages : 1 459
    Points : 4 634
    Points
    4 634
    Par défaut
    Bonjour,

    Est-ce que ce ne serait pas un arrondi des heures cachées ?

    Quand on fixe une date sans préciser l'heure, je ne suis pas sûr que l'heure soit forcée à 0.

    Alors quand on fait incDay, s'il fait un arrondi et que l'heure cachée est supérieure à 0.5 (midi) il va y avoir un jour en trop.

    Ce n'est qu'une hypothèse.

    Salutations

  9. #9
    Modérateur
    Avatar de tourlourou
    Homme Profil pro
    Biologiste ; Progr(amateur)
    Inscrit en
    Mars 2005
    Messages
    3 877
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Biologiste ; Progr(amateur)

    Informations forums :
    Inscription : Mars 2005
    Messages : 3 877
    Points : 11 373
    Points
    11 373
    Billets dans le blog
    6
    Par défaut
    https://wiki.freepascal.org/TDateTime nous montre que le TdateTime ne représente rien entre -1 et 0 et la routine cherche à éliminer cette durée fantôme quand on se contente d'additionner.
    Je continue de ne pas comprendre pourquoi elle bute sur une limite particulière.
    J'ai du mal avec la représentation des flottants, malgré le site https://baseconvert.com/ieee-754-floating-point.

  10. #10
    Membre averti
    Profil pro
    Inscrit en
    Septembre 2005
    Messages
    299
    Détails du profil
    Informations personnelles :
    Âge : 55
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations forums :
    Inscription : Septembre 2005
    Messages : 299
    Points : 330
    Points
    330
    Par défaut
    @Guesset, non, pour les dates sans heures, il y a bien 0 dans la partie décimale.

    Citation Envoyé par Alcatîz Voir le message
    J'ai fait d'autres essais :...
    Je suis allé jusqu'à l'an 2888, soit 400.000 itérations : l'erreur ne survient qu'au jour 38818 (et affecte tous les jours suivants).
    Ceci dit, les chances pour que l'on découvre ce bug étaient minces : on calcule rarement une date à partir de l'an 1793.
    [/EDIT]
    Oui, merci pour ces tests approfondis, je suis partiellement d'accord, il faut être généalogiste (un français sur 2 a déjà fait des recherches sur ses ancêtres) ET informaticien ET vouloir concilier les deux mondes

    Et puis, on peut extrapoler le problème de façon plus générale : toute fonction incday qui s'applique à une date antérieure au 01/01/1900 (même la veille) et qui après l'incrémentation passe après ce 1er janvier bugue (je viens de vérifier avec -1 pour la date et +4 et +5 dans l'incrément)

    @Tourlourou : Je pense que cette fonction MayBeSkipTimeWarp avait effectivement pour objectif de gérer cet intervalle -1.0..0.0, je suis de plus en plus convaincu qu'elle ne le fait pas bien

  11. #11
    Modérateur
    Avatar de tourlourou
    Homme Profil pro
    Biologiste ; Progr(amateur)
    Inscrit en
    Mars 2005
    Messages
    3 877
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Biologiste ; Progr(amateur)

    Informations forums :
    Inscription : Mars 2005
    Messages : 3 877
    Points : 11 373
    Points
    11 373
    Billets dans le blog
    6
    Par défaut
    Delphi 11 ne fait pas l'erreur, mais il ne reprend pas cette logique. IncDay y incrémente les jours par leur durée en millisecondes en appelant ce code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    function IncMilliSecond(const AValue: TDateTime;
      const ANumberOfMilliSeconds: Int64 = 1): TDateTime;
    var
      TS: TTimeStamp;
      TempTime: Comp;
    begin
      TS := DateTimeToTimeStamp(AValue);
      TempTime := TimeStampToMSecs(TS);
      TempTime := TempTime + ANumberOfMilliSeconds;
      TS := MSecsToTimeStamp(TempTime);
      Result := TimeStampToDateTime(TS);
    end;

  12. #12
    Expert confirmé

    Homme Profil pro
    Directeur de projet
    Inscrit en
    Mai 2013
    Messages
    1 459
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Directeur de projet
    Secteur : Service public

    Informations forums :
    Inscription : Mai 2013
    Messages : 1 459
    Points : 4 634
    Points
    4 634
    Par défaut
    Bonjour,

    Je pense que le problème vient de la complexité du stockage.

    Si j'ai bien compris, même si c'est un double qui le supporte, le choix fait est n°_jour + heure avec heure toujours positif en fraction de jour. Il en résulte une différence entre durée et datetime :
    • Une durée de -1.25 représente -30 heures.
    • Mais une datetime de -1.25 représente l'heure 6 du jour -1 (soit en numérique -0.75 ou -18 heures par rapport à la date 0)
    • Le problème est que la différence entre deux datetimes est une durée (changement de représentation) et, de même, calculer une nouvelle date ajoute/retranche une durée (changement de représentation).

    Il y a de quoi se mélanger les pinceaux.
    Soit on gère, comme ici, les cas à cheval sur la frontière avec le risque d'oublier des cas, soit on exprime tout en durées (relatives à une date pivot pour les date) mais c'est plus lourd en calcul... ...et l'historique est là.

    Ce genre de problème se pose quand on mêle des n° (qui ne sont que des repères donc normalement non destinés au calcul) et des quantités. On a le même problème avec les températures, si la différence peut sembler signifiante (il fait 3°C de plus qu'hier), la somme ne l'est pas.

    Salutations

  13. #13
    Expert confirmé

    Homme Profil pro
    Directeur de projet
    Inscrit en
    Mai 2013
    Messages
    1 459
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Directeur de projet
    Secteur : Service public

    Informations forums :
    Inscription : Mai 2013
    Messages : 1 459
    Points : 4 634
    Points
    4 634
    Par défaut Tic Toc Tic Tic Toc ...
    Bonjour,

    Il ne faudrait pas que nous fassions une fixette sur les dates très anciennes car les calendriers ne sont pas des modèles de stabilité :
    • Il n'y a pas d'année 0 donc, entre le de début des années -1 et +1 il n'y a qu'un an.
    • En 1582, en France nous sommes passés directement du 9 au 20 décembre. Et d'autres pays ont pratiqué de même à des périodes différentes.

    De nos jours aussi il y a de temps à autre (sic) des insertions d'une seconde intercalaire.

    Précision et exactitude ne sont pas toujours amies.

    Salutations

  14. #14
    Membre averti
    Profil pro
    Inscrit en
    Septembre 2005
    Messages
    299
    Détails du profil
    Informations personnelles :
    Âge : 55
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations forums :
    Inscription : Septembre 2005
    Messages : 299
    Points : 330
    Points
    330
    Par défaut
    Ah, c'est sympa d'avoir vérifié sur Delphi (qui est le langage que j'utilisais sur le plan professionnel), et ça illustre donc qu'il peut y avoir une meilleure façon de coder la fonction.

    Citation Envoyé par Guesset Voir le message
    ...En 1582, en France nous sommes passés directement du 9 au 20 décembre. Et d'autres pays ont pratiqué de même à des périodes différentes.
    ...Précision et exactitude ne sont pas toujours amies...
    Oui 1582, c'est lié au calendrier Julien (la fonction JulianDateToDateTime a été mise au point à cet effet, et rien que le fait que cette fonction existe illustre à quel point ces dates anciennes sont susceptibles d'être utilisées dans des programmes).

    Mais, sans vouloir être perfectionniste, d'ajouter 5 jours à une date et d'obtenir une autre date 6 jours plus loin, j'appelle quand même ça un bug, pas une simple inexactitude.
    Je vais faire un signalement à l'équipe de dev.
    On n'en est pas encore à ce stade, https://www.manaps.com/6-bugs-inform...rophiques.html mais des dates pas bien gérées, ça peut quand même avoir des répercussions pas toujours anodines.

    En tout cas, merci à tous les intervenants de s'être penché sur le problème, ça montre que la communauté Lazarus sur ce forum est bien active et altruiste, et l'ensemble des idées a permis de trouver la cause... Du coup, je clos le sujet.
    Passez de bonnes fêtes de fin d'année

  15. #15
    Membre confirmé
    Profil pro
    Inscrit en
    Avril 2010
    Messages
    248
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2010
    Messages : 248
    Points : 538
    Points
    538
    Par défaut
    Bonjour,

    Le problème n'est effectivement pas simple à cause du type TDateTime. Comme dit précédemment, la partie décimale doit être convertie en valeur absolue pour calculer la portion du jour, et la conversion d'un entier en double introduit presque toujours une petite erreur. C'est pourquoi la procédure MaybeSkipTimeWarp est toujours appelée après un ajout ou une soustraction de TDateTime.
    TDateTimeEpsilon est là pour compenser l'erreur de la conversion en Double, mais il semble bien que avec Windows cette erreur soit supérieure à 2.2204460493e-16.
    Pour vous en convaincre j'ai fait le petit programme suivant intégrant les 2 procédures avec la version 2.3 de Lazarus (l'erreur ne date pas de la version 3):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
     
     
    procedure TForm1.Button1Click(Sender:TObject);
    var
      i,j:integer;
      debut,suivant:TDateTime;
      DateTimeEpsilon:Double;
    begin
      debut:=EncodeDate(1899,12,29);
    //  debut:=EncodeDate(1900,1,5);
      for j:=0 to 1 do
      begin
        if j=0 then
          DateTimeEpsilon:=2.2204460493e-16 //valeur d'origine
        else
          DateTimeEpsilon:=0.001/MSecsPerDay;
        Memo1.Append('DateTimeEpsilon : '+DateTimeEpsilon.ToString());
        for i:=0 to 10 do
        begin
          suivant:=debut+i;
          if (debut>=0) and (suivant<-DateTimeEpsilon) then
            suivant:=int(suivant-1.0+DateTimeEpsilon)-frac(1.0+frac(suivant))
          else if (debut<=-1.0) and (suivant>-1.0+DateTimeEpsilon) then
            suivant:=int(suivant+1.0-DateTimeEpsilon)+frac(1.0-abs(frac(1.0+suivant)));
          Memo1.Lines.Append(IntToStr(i)+' : '+DateTimeToStr(suivant));
        end;
      end;
    end;
    Pour activer le calcul dans le cas "if (debut>=0) and (suivant<-DateTimeEpsilon) then" utilisez les lignes en commentaires et remplacez " suivant:=debut+i;" par " suivant:=debut-i;".
    Sous Windows, il y a des erreurs dans les 2 cas avec la valeur d'origine de DateTimeEpsilon, mais ces erreurs disparaissent en donnant à DateTimeEpsilon la valeur d'une microseconde.
    Voilà pour l'avis de l'informaticien amateur. Maintenant celui du généalogiste amateur:
    Le calendrier républicain n'est défini que du 1er vendémiaire an I (22 septembre 1792) au 10 nivôse an XIV (31 décembre 1805). Donc l'an CVIII (108) est une date invalide.
    Avec les 2 casquettes: en généalogie l'heure n'est pas toujours donnée (et même rare dans les registre paroissiaux) et n'a d'intérêt que pour ordonner les naissances de jumeaux. J'ai préféré recréer un type date spécifique du type interger, de même origine que le TDateTime, ce qui implique une conversion de certaines fonctions n'acceptant que les TDateTime. Un autre champ de type TTime permet éventuellement d'enregistrer l'heure de l'événement.

    André

  16. #16
    Expert confirmé

    Homme Profil pro
    Directeur de projet
    Inscrit en
    Mai 2013
    Messages
    1 459
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Directeur de projet
    Secteur : Service public

    Informations forums :
    Inscription : Mai 2013
    Messages : 1 459
    Points : 4 634
    Points
    4 634
    Par défaut
    Bonjour,

    Il faut faire très attention avec les flottants car ils flottent.

    Quand on prend 2.2204460493e-16 et qu'on l'additionne (soustrait) avec un entier non nul on passe par une normalisation (en fait une mise à la même puissance) qui engendre une perte de résolution car les doubles ont une précision finie.

    Prenons 1 + 2e-16. La normalisation s'apparente à (1016 +2) 10-16 soit une mantisse d'environ 53 bits. Or les doubles ont une mantisse de 56 bits (on ne chipotera pas sur le premier 1 qui reste implicite) ce qui laisse 3 bits pour une valeur plus fine que 2. En résumé, sauf erreur, 2.2204460493e-16 sera tronquée aux environs de 2.2e-16. Il faut espérer ne pas avoir besoin des autres chiffres

    Salutations

  17. #17
    Membre averti
    Profil pro
    Inscrit en
    Septembre 2005
    Messages
    299
    Détails du profil
    Informations personnelles :
    Âge : 55
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations forums :
    Inscription : Septembre 2005
    Messages : 299
    Points : 330
    Points
    330
    Par défaut
    Merci cher confrère, pour cette étude fouillée (et merci Guesset pour ces exemples).
    Je vais faire suivre le raisonnement sur DateTimeEpsilon.

    En fait, tous ces soucis sont liés à l'écriture d'une fonction de conversion entre date républicaine que j'ai voulu élargir au-delà de la période où cette date était en vigueur (par simple curiosité intellectuelle).
    Sachant qu'il y a débat sur ce qu'auraient été les années bissextiles dans ce calendrier élargi (le 6° jour complémentaire) et qu'effectivement les types de données manipulées sont des dates sans heure (mais les fonctions de type incday réclament le type tDateTime/double), le problème n'en est pas vraiment un.

    Il subsiste cependant le bug hors contexte généalogique.

    Je pensais intuitivement qu'il fallait une valeur de DateTimeEpsilon encore plus petite (proche de 0) mais si je comprends bien, c'est l'inverse.
    Alors, j'ai fait quelques verifs pour m'en assurer.
    En jonglant sur la valeur de l'exposant (1e-14), on finit quand même par reproduire le bug du jour sauté mais en ajoutant plus de jours (129 jours ajoutés à -1)
    Avec 1e-13, c'est 1025 (environ 3 ans), avec 1e-12, c'est 16385 (45 ans), et avec le epsilon que tu proposes dans ton code, le jour sauté arrive en ajoutant 131073 (359 ans).

    Je ne suis plus assez costaud en mathématique pour expliquer le problème lié à ces virgules flottantes. Encore une fois, les chances sont très faibles de tomber sur ce cas de figure mais si la prochaine sonde à partir hors du système solaire utilise la fonction incday pour envoyer des données, je m'en voudrais de ne pas avoir signalé le problème !

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Bug sur la fonction API ShGetValue
    Par yann458 dans le forum Windows
    Réponses: 2
    Dernier message: 22/10/2014, 17h41
  2. Bug sur la fonction financière TRIM ?
    Par pedromarga dans le forum Excel
    Réponses: 2
    Dernier message: 23/07/2011, 16h16
  3. [POO] Bug sur une fonction récursive : renvoit undefined
    Par zaboug dans le forum Général JavaScript
    Réponses: 4
    Dernier message: 23/06/2008, 14h10
  4. Bug sur les fonctions virtuelles
    Par Anthony.Desvernois dans le forum C++
    Réponses: 4
    Dernier message: 28/01/2008, 17h30
  5. Rapport de bug sur les fonctions de XDebug
    Par lryo79 dans le forum Zend Studio
    Réponses: 8
    Dernier message: 14/06/2007, 15h35

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo