2010-11-21 2 views
6

Tentative d'émulation à la main du roulement d'une horloge de 24 heures (avec math ou utilisation des classes de timespan). La partie incrémentée était facile à comprendre comment rouler de 23h00 à 0h00 et de, mais le faire pour aller dans l'autre sens s'avère être vraiment déroutant. Voici ce que j'ai jusqu'à présent:C#: décrémentation d'une horloge à l'aide du module math

static void IncrementMinute(int min, int incr) 
{ 
    int newMin = min + incr, 
       hourIncrement = newMin/60; 

    //increment or decrement the hour 
    if((double)newMin % 60 < 0 && (double)newMin % 60 > -1) 
     hourIncrement = -1; 

    Console.WriteLine("Hour increment is {0}: ", hourIncrement); 
} 

Le problème que la constatation im est en marche arrière, si le module d'entre nombres, il ne sera pas décrémenter correctement. Exemple: il est 12:00 et vous soustrayez 61 minutes, nous savons que l'heure serait 10:59 car l'heure devrait reculer d'une heure pour aller de 12:00 à 11:59, puis de nouveau pour partir de 11:00 à 10:59. Malheureusement la façon de le calculer: newMin% 60 dans ce cas, ne récupère que le rollback de la première heure, mais puisque le second rollback est techniquement -1.0166 comme un reste, et puisque mod ne retourne qu'un nombre entier, son arrondi. Je suis sûr de manquer quelques maths de base ici, mais quelqu'un pourrait-il m'aider?

EDIT: J'ai écrit ceci un certain nombre de façons longues et courtes. Certains sont plus proches que d'autres, mais je sais que c'est plus simple qu'il n'y paraît. Je sais que celui-ci semble un peu "wtf était-il en train de faire", mais vous devriez être capable de voir ce que je suis en train de faire. Incrémenter une horloge et la faire basculer de 23:59 à 0:00 est facile. Aller en arrière s'est avéré être pas si facile. OK, voici le incrementMinute avec le rollover. Simple. Mais essayez d'aller en arrière. Ne fonctionne pas

static void IncrementMinute(int min, int incr) 

     { 
      int newMin = min + incr, 
       hourIncrement = newMin/60; 

      min = newMin % 60; 

      Console.WriteLine("The new minute is {0} and the hour has incremented by {1}", min, hourIncrement); 
     } 
+1

Pouvez-vous ajouter un peu plus de détails à ce, notamment un exemple d'utilisation et de l'état attendu de la variable globale? – steinar

+0

Quelles sont les valeurs de 'min' et' incr'? – ChrisF

+0

Je crois que j'ai donné ces exemples. Fondamentalement, comment puis-je coder la décrémentation pour avoir 12:00 - 0:61 = 10:59 et le faire fonctionner pour les incréments aussi bien. – Sinaesthetic

Répondre

1

J'irais quelque chose d'un peu plus simple

public class Clock 
{ 
    public const int HourPerDay = 24; 
    public const int MinutesPerHour = 60; 
    public const int MinutesPerDay = MinutesPerHour * HourPerDay; 

    private int totalMinutes; 

    public int Minute 
    { 
     get { return this.totalMinutes % MinutesPerHour; } 
    } 

    public int Hour 
    { 
     get { return this.totalMinutes/MinutesPerHour; } 
    } 

    public void AddMinutes(int minutes) 
    { 
     this.totalMinutes += minutes; 
     this.totalMinutes %= MinutesPerDay; 
     if (this.totalMinutes < 0) 
      this.totalMinutes += MinutesPerDay; 
    } 

    public void AddHours(int hours) 
    { 
     this.AddMinutes(hours * MinutesPerHour); 
    } 

    public override string ToString() 
    { 
     return string.Format("{0:00}:{1:00}", this.Hour, this.Minute); 
    } 
} 

Exemple d'utilisation:

new Clock().AddMinutes(-1); // 23:59 
new Clock().AddMinutes(-61); // 22:59 
new Clock().AddMinutes(-1441); // 23:59 
new Clock().AddMinutes(1);  // 00:01 
new Clock().AddMinutes(61); // 01:01 
new Clock().AddMinutes(1441); // 00:01 
+0

Dans votre fonction AddMinutes: s/if/while /, au cas où l'utilisateur ajoute plus d'un jour de valeur négative. – jtdubs

+0

@jtdubs, 'this.totalMinutes% = MinutesPerDay;' prend soin de cela;) – Diadistis

+0

@Sinaesthetic, si vous allez utiliser cette classe, gardez à l'esprit que ce n'est pas thread-safe. – Diadistis

0

Les mathématiques modulaires ne sont définies que pour les entiers. Si vous essayez de mélanger l'arithmétique modulaire avec des nombres réels, vous ne réussirez pas. Vous devez trouver une approche mathématique différente.

+0

le casting réel était juste pour entrer dans la structure de contrôle. J'essayais de m'assurer qu'il soustrairait au moins 1 heure, mais j'ai découvert que cela causerait des problèmes avec le décrément suivant s'il était inférieur à 1 heure complète. déroutant ... – Sinaesthetic

+0

oui, mais conceptuellement vous voulez ce reste. Cela ne se produira pas en utilisant l'arithmétique modulaire à moins de séparer les heures et les minutes avant d'appliquer le module. – joejoeson

+0

les heures et les minutes sont séparées avant d'appliquer le module. – Sinaesthetic

1

Vous pouvez essayer de calculer les deux tranches d'une minute et une heure d'abord, la manipulation puis les cas où les nouvelles minutes traverse une limite d'heure, quelque chose comme ceci:

int hourIncrement = incr/60; 
int minIncrement = incr % 60; 

int newMin = min + minIncrement; 

if (newMin < 0) 
{ 
    newMin += 60; 
    hourIncrement--; 
} 
else if (newMin > 60) 
{ 
    newMin -= 60; 
    hourIncrement++; 
} 

Modifier

J'aime @Ben Voigts répondre, mais se demandait s'il y aurait une différence de performance. J'ai couru l'application de console ci-dessous pour les chronométrer tous les deux, et étais un peu surprise par les résultats.

  • 40 ms pour le code ci-dessus
  • 2876 ms pour la réponse

Ben Cela a été fait dans une version release. Quelqu'un d'autre peut-il exécuter ceci et confirmer? Est-ce que je fais des erreurs dans la façon dont je les chronomètre?

using System; 
using System.Diagnostics; 

namespace ConsoleApplication1 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      Stopwatch sw = new Stopwatch(); 

      int max = 100000000; 

      sw.Start(); 
      for (int i = 0; i < max; i++) 
       IncrementMinute1(0, -61); 
      sw.Stop(); 

      Console.WriteLine("IncrementMinute1: {0} ms", sw.ElapsedMilliseconds); 

      sw.Reset(); 

      sw.Start(); 
      for (int i = 0; i < max; i++) 
       IncrementMinute2(0, -61); 
      sw.Stop(); 

      Console.WriteLine("IncrementMinute2: {0} ms", sw.ElapsedMilliseconds); 

      Console.ReadLine(); 
     } 

     static void IncrementMinute1(int min, int incr) 
     { 
      int hourIncrement = incr/60; 
      int minIncrement = incr % 60; 

      int newMin = min + minIncrement; 

      if (newMin < 0) 
      { 
       newMin += 60; 
       hourIncrement--; 
      } 
      else if (newMin > 60) 
      { 
       newMin -= 60; 
       hourIncrement++; 
      } 
     } 

     static void IncrementMinute2(int min, int incr) 
     { 
      min += incr; 
      int hourIncrement = (int)Math.Floor(min/60.0); 
      min -= hourIncrement * 60; 
     } 
    } 
} 
+0

à première vue, cela ne fonctionnerait pas si vous êtes allé plus d'une heure en arrière. C'est un peu où j'étais il ya environ une heure :( – Sinaesthetic

+0

@Sinaesthetic, peut-être que je suis un malentendu, mais si je commence par 'min == 0' et' incr = -121', je reçois 'newMin == 59 –

+0

@adrift: Il me semble que cela devrait fonctionner, mais c'est plus compliqué que nécessaire –

0

Essayez

 int newMin = min + incr, 
      hourIncrement = (int)Math.Floor(newMin/60.0); 

     min -= hourIncrement * 60; 

Le problème essentiel est que vous voulez hourIncrement à arrondir vers le bas, mais tours de division entière vers zéro. Ils sont les mêmes avec des nombres positifs, mais pas pour négatifs ...

EDIT (se débarrasser de la variable supplémentaire inutile):

min += incr; 
    int hourIncrement = (int)Math.Floor(min/60.0); 
    min -= hourIncrement * 60; 

EDIT2 (éviter l'arithmétique à virgule flottante):

min += incr; 
    int hourIncrement = min/60; 
    min -= hourIncrement * 60; 
    if (min < 0) { min += 60; --hourIncrement; } 
+0

ok Math.Floor est ce que je cherchais. Ne résout pas le retour en arrière, mais c'est la partie sur laquelle je me débattais le plus. Je pense que je peux l'obtenir d'ici merci. – Sinaesthetic

+0

J'ai fourni du code pour prendre en charge la restauration des minutes, mais j'ai été mélangé par le fait que vous n'étiez pas en train de mettre la nouvelle minute dans 'newMin'. Voir http: // ideone.com/i3Yom –

0

Pourquoi compliquer les choses

public System.Timers.Timer timer = new System.Timers.Timer(1000); 
public DateTime d; 

public void init() 
{ 
timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed); 

d = new DateTime(2011, 11, 11, 23, 59, 50); 
d=d.AddHours(1); 
Console.Writeline(d); 
d=d.AddHours(-2); 
Console.Writeline(d); 
timer.Enabled = true; 
} 
    void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) 
    { 
     this.Dispatcher.Invoke(DispatcherPriority.Normal, (Action)(() => 
     { 
      MoveClockHands(); 
      d=d.AddSeconds(1); 
      Console.WriteLine(d); 

     })); 
    } 

    void MoveClockHands() //12 hours clock 
    (
     s=d.Second * 6; 
     m=d.Minute * 6; 
     h=0.5 * ((d.Hour % 12) * 60 + d.Minute) 
    }