2009-04-20 8 views
2

Étant donné que DateTime.AddDays() prend un double paramètre, je suis préoccupé par le fait que lorsque vous ajoutez un jour, il peut y avoir quelques erreurs d'arrondi. Par exemple Disons que j'ai la boucle suivante:Quelle est la précision de DateTime.AddDays?

DateTime Now = DateTime.Today; 
for (int i = 0; i < 365; ++i) 
{ 
    Now = Now.AddDays(1); 
    // do something 
} 

Je crains que maintenant pourrait commencer à la dérive loin de minuit. Je suis toujours tenté de faire quelque chose comme ce qui peut être un peu plus lent, mais allègent ma paranoïa:

for (int i = 0; i < 365; ++i) 
{ 
    Now = Now.AddDays(1.01).Date; 
    // do something 
} 
+0

Si vous appelez votre méthode proposée avec '= maintenant 23: 59', il en résultera une mauvaise résultat. – mafu

Répondre

11

Comme DateTime stocke la date à l'intérieur d'un entier de 64 bits où une coche représente 100 nanosecondes, il n'y a pas de risque d'erreur. Un jour a 864 000 000 000 ticks et Double a une précision d'au moins 15 chiffres. Ainsi, chaque erreur disparaît en arrondissant en ticks car Double a une résolution supérieure à une coche si 1.0 équivaut à un jour.

Ceci ne serait pas vrai pour AddYears(), car Double n'a pas assez de précision pour représenter une coche si 1.0 équivaut à un an. Mais si vous regardez la classe DateTime, vous voyez que la conception respecte ce fait - AddMonths() et AddYears() ont à la fois des arguments entiers et non à virgule flottante.

Pour vérifier cela, exécutez simplement le code suivant.

DateTime now = DateTime.Now; 

// Displays 864000000000 
Console.WriteLine(now.AddDays(1.0).Ticks - now.Ticks); 
0

Que voulez-vous dire à la dérive loin de minuit?

Lors de l'exécution, le 1er code a la date de la date de début 20/04/2010 12:00:00 AM.
Je pense que c'est ce que vous attendez. N'est-ce pas?

0

Comme dans votre exemple, j'ai ajouté 1 jour à une variable DateTime 100 000 fois et toujours terminé avec une date avec une valeur de minuit. Il semble qu'il n'y ait aucune raison de s'inquiéter.

Je suis sûr qu'ils arrondissent à des tics de temps, ce qui élimine tout problème de dérive.

1

Le nombre de jours est multiplié par un nombre entier (l'échelle) et ensuite ajouté au nombre de tiques stockées par le DateTime. Lorsque vous passez un nombre entier, vous finirez par ajouter un nombre entier de graduations. D'autre part, je ne sais pas ce que fait .NET sur les secondes intercalaires ... Je suppose qu'il utilise un modèle assez simple qui ne dérange pas avec eux, laissant votre code correct.

N'oubliez pas que l'ajout de fuseaux horaires ajoute une complication supplémentaire - ajouter un jour localement peut ajouter plus ou moins d'un jour en termes d'UTC, et vice versa.

+0

+1 pour les secondes intercalaires Jon;) –

2

En général, je pense que vous avez raison de vous inquiéter d'arrondir avec des doubles puisque tous les nombres réels ne sont pas absolument exprimables avec un double - par exemple, si vous deviez ajouter 1/3 d'un jour trois fois, vous pourriez pas finir avec exactement un jour d'avance. Cependant, dans ce cas, 1 est un nombre qui est absolument expressible et que vous multipliez simplement par un autre nombre qui est aussi absolument exprimable dans un double (le nombre de ticks dans une journée), vous devriez être ok. Le deuxième échantillon est probablement exagéré.

Exemple:

DateTime now = DateTime.Today; 
for (int i = 0; i < 7; ++i) 
{ 
    for (int j = 0; j < 7; ++j) 
    { 
     now = now.AddDays(1/7.0); 
    } 
} 
Console.WriteLine(DateTime.Today); 
Console.WriteLine(now); 

Résultats dans (sur) 4/20/2009

4/20/2009 12:00:00 AM 
4/26/2009 11:59:59 PM 
+0

Peut-être que vous devriez ajouter une note à votre exemple. Le fait que l'on puisse être doublé avec Double peut mener à la conclusion que cela est vrai pour tous les entiers, ce qui est bien sûr faux. –

+0

Vous devez ajouter un très grand nombre de jours pour que cela soit jamais un problème. Je suppose que vous déborderiez d'abord la structure de la date, car un double a au moins 15 chiffres significatifs. – tvanfosson