Calcul de vitesse, intensité et conversion en durée (minutes / secondes)

Bonjour à tous,

je suis un débutant en programmation et je regarde les cours très pédagogues et très sympathiques de Maxime pour me mettre le pied à l’étrier.

Je me fais les dents pour apprendre sur une petite app pour les sportifs et mes lycéens (prof d’EPS) qui permettrait simplement à l’aide d’un slider de calculer un temps de passage en fonction d’une vitesse de course et d’une distance à parcourir.
ça marche très bien sur excel… mais j’ai de grosses erreurs dans mon app. J’ai du mal à savoir si ça vient du calcul, des paramètres du spider ou de la méthode de conversion du temps en Int en 00:00 avec le modulo.

voici mon code:
class VMAViewController: UIViewController, UITextFieldDelegate {

@IBOutlet weak var ui_vma: UITextField! // vitesse de course
@IBOutlet weak var ui_purcent: UILabel!  // % affiché par le slider
@IBOutlet weak var ui_distanceChargeLabel: UILabel!   // résultat à afficher en min/sec
@IBOutlet weak var ui_distance: UITextField!   // distance de course en m.

// affichage dans le label le % de travail sildé
@IBAction func slider(_ sender: UISlider) {
    ui_purcent.text = "\(String(Int(sender.value))) %"
}


@IBAction func test(_ sender: UISlider) {
    // validation que VMA et Distance ne soit pas vide avec optionel
    
    // vma en double
    if let inputStringVma:String = ui_vma.text,
        let inputVmaDouble:Double = Double(inputStringVma){
        
        // distance en double
        if let inmputStringDistance:String = ui_distance.text,
            let inputDistanceDouble:Double = Double(inmputStringDistance){
            
        // calcul si ces variables peuvent être créees:
                    // intialisation des variables
            var timeBaseDouble:Double = 0
            let purcentCoef:Double = (Double(sender.value)) / 100
            var purcentLess:Double = 0
            var calculDuree:Double = 0
                    // conversion des km/H en m/s
                timeBaseDouble = (inputDistanceDouble / (inputVmaDouble / 3.6))
                    // on retranche au temps de base le % issu du coefficient donné par le slider
                purcentLess = timeBaseDouble - (timeBaseDouble * purcentCoef)
                    // on addictionne le temps de base
                calculDuree = timeBaseDouble + purcentLess
        // application et affichage du % dans le calcul par le slider
        ui_distanceChargeLabel.text = returnTime(temps: Int(calculDuree))
            }
    }

}
// retourner le temps à réaliser en fonction de la VMA et de la distance exprimée en 00:00
func returnTime (temps:Int) → String {
let minutes = Int(temps) / 60
let seconds = Int(temps) % 60
return String(format: « %02i:%02i », minutes, seconds)

je me casse à la tête dessus depuis 3 jours!

Si quelqu’un a une piste, merci d’avance!!

58

Dans cette capture j’ai déjà une erreur de 6 sec.
03

A première vue, ton erreur vient de la manière dont tu calcules le nombre de secondes.

L’opérateur modulo (%) est conçu pour calculer le reste de la division d’un Entier (nombre entier) et non d’un Double (nombre à virgule). Pour faire ton calcul de Modulo tu forces la conversion du Double en Entier, ce qui ELIMINE LA PARTIE FRACTIONNAIRE et donc une partie de l’information. D’où l’erreur de calcul dans ton application.

// Pas bien !!
let seconds = Int(temps) % 60

Pour récupérer le reste de la division d’un Double, il faut utiliser l’opérateur .remainder(dividingBy:).

let resteDivision = valeurDouble.remainder(dividingBy: 60.0)


L’élimination de la partie factionnaire lors de la conversion d’un Double vers un Entier pose un autre problème, la valeur étant systématiquement arrondie au nombre en dessous.

10.4 (Double) conversion en Entier => 10
10.9 (Double) conversion en Entier => 10
10.9999 (Double) conversion en Entier => 10 (pas gentil pour tes élèves perdant 0,9999 secondes sur leurs scores).

Avant de convertir ton Double en Entier, il faut arrondir sa valeur en utilisant une fonction prévue spécialement pour ça : .rounded(). Elle nécessite un paramètre indiquant la manière dont l’arrondi dont se faire (à la valeur au dessus, à la valeur en dessous, ou à la valeur la plus proche).

let nbSecondesArrondis = Int(nbSecondes.rounded(.toNearestOrEven))

Avec l’arrondi à la valeur la plus proche, 6.4999 devient 6. Et 6.5000001 devient 7. C’est la formule d’approximation que le cerveau utilise naturellement.


Voici une piste balisée pour toi :

        let vitesseHoraire = 14000.0
        let distance = 4000.0
        
        // temps du parcours en heure
        let tempsHeure = distance/vitesseHoraire
        // Temps du parcours en secondes
        let tempsSeconde = distance/vitesseHoraire*3600
        
        // Calcul nb minutes (Entier)
        let nbMinutes = Int(tempsSeconde/60)
        // Calcul du nombre de secondes restantes - Double)
        let nbSecondes = tempsSeconde.remainder(dividingBy: 60.0)
        // Approximation à la seconde
        let nbSecondesArrondis = Int(nbSecondes.rounded(.toNearestOrEven))
        
        print ("temps (en heure) : ", tempsHeure)
        print ("temps (en seconde) : ", tempsSeconde)
        print ("-----")
        print ("nombre de minutes : ", nbMinutes)
        print ("nombre de secondes (brut) : ", nbSecondes)
        print ("nombre de secondes (arrondis) : ", nbSecondesArrondis)

Affichage dans le simulateur :

temps (en heure) : 0.285714285714286
temps (en seconde) : 1028.57142857143

nombre de minutes : 17
nombre de secondes (brut) : 8.57142857142844
nombre de secondes (arrondis) : 9

Un détail technique :

  • Pas besoin d’écrire Double partout dans le code. Il suffit d’écrire une valeur à virgule pour que Xcode la considère comme un Double. C’est pourquoi on voit souvent des chiffres suivis d’un .0 dans les exemples de codes.

let distance = 4000.0 // Xcode sait que c’est un Double

Merci Draken pour ta réponse.
J’ai en conséquence modifié mon code:
@IBAction func buttonConversion(_ sender: Any) {
// vérification des optionnelles et passage en Double
if let inputStringVma:String = ui_vma.text,
let inputVmaDouble:Double = Double(inputStringVma){

        if let inmputStringDistance:String = ui_distance.text,
            let inputDistanceDouble:Double = Double(inmputStringDistance){

            
        // intialisation des variables
            var timeBaseDouble:Double = 0
           
       
        // conversion des km/H en m/s
                let timeHoraire = inputVmaDouble * 1000
                timeBaseDouble = inputDistanceDouble / timeHoraire * 3600
        // arrondis
                let timebaseArrondie = timeBaseDouble.rounded(.toNearestOrEven)
        // affichage dans le textField
                ui_labelButtonConversion.text = returnTime(temps: timebaseArrondie)


}
}
}
func returnTime (temps:Double) -> String {
    let nbMinutes = Int(temps / 60)
    // seconcdes restantes
    let nbSecondes = temps.remainder(dividingBy: 60.0)
    // arrondi à la seconde
    let nbSecondesArrondies = Int(nbSecondes.rounded(.toNearestOrEven))
    return String(format: "%02i:%02i", nbMinutes, nbSecondesArrondies)

Ça fonctionne. Cependant, si je mets une distance de - de 460m j’ai un résultat de type 01:-17, 01-02, etc. C’est bizarre.

Enfin, lorsque j’applique des pourcentages, les résultats sont délirants. J’ai abandonné le slider pour l’infant car en le déplaçant très légèrement sur une valeur, j’ai trois résultats différentes… et je ne comprend pas le fonctionnement.

Quand tu as des problèmes comme ça, il faut utiliser l’instruction print() un peu partout dans le code, pour afficher le contenu des variables au fur et à mesure du déroulement des opérations.

let var1 = 1765
let var2 = 897
let resutat1 = faireQuequeChose avec var1 et var2
print (« resultat1 : « , resultat1)

let var3 = 876
let resultat2 = resultat1/ver3
print (« resultant 2 : « , resultat2)

etc ..

Cela permet de voir l’endroit où les choses deviennent étranges. Par exemple, tu devrais afficher la valeur du spider, il est possible que l’erreur provienne d’une mauvaise initialisation des bornes inférieurs et supérieurs.

Oups, c’est de ma faute. J’ai lu la documentation en diagonale au lieu de lire jusqu’au bout. remainder() est une fonction très bizarre en fait, dont je ne comprend pas l’intérêt.

Elle fonctionne correctement tant que le reste de la division est compris entre 0 et 30.
Par contre, entre 31 et 60, elle donne un reste sous forme d’une valeur négative qu’il faut soustraire à 60 ! Je n’ai jamais vu un truc pareil. Il y a certainement une explication mathématique à ça, mais laquelle ?

C’est un exemple de bug aléatoire, les pires qui existent. Elles ne se produisent que pour certaines valeurs et pas pour d’autres. Quand la distance est de 4.000 mètres, le reste de la division est compris entre 0 et 30, donc ça marche. Pour d’autres valeurs c’est le clash…

Remplace :
let nbSecondes = tempsSeconde.remainder(dividingBy: 60.0)

par :

    let nbSecondes = tempsSeconde.truncatingRemainder(dividingBy: 60.0)

Et cela fonctionnera.

truncatingRemainder() permet d’obtenir le reste de la division entière sans cette histoire étrange de notation négative.

Distance : 460.0

temps (en heure) : 0.0328571428571429
temps (en seconde) : 118.285714285714

nombre de minutes : 1
nombre de secondes (brut) : 58.2857142857143
nombre de secondes (arrondis) : 58

L’une des raisons pour laquelle je répond aux problèmes des gens sur ce forum, c’est que cela me fait sortir de ma "zone de confort intellectuelle ». Grâce à toi, j’ai appris quelque chose sur la division des nombres à virgule, un problème potentiel que je ne soupçonnais pas. Merci !

En tous cas, je te remercie de ton aide.
Je pensais me lancer dans un truc simple, mais ça me rappelle de façon assez directe de ne pas vouloir griller les étapes. La méthode par print m’a permis de voir 2 erreurs de calcul.

Concernant les calculs de durée, j’étais en train de te répondre au moment de ton message pour le même sujet! :wink:

En utilisant formTruncating, il se pose un nouveau problème. Ma fonction returnTime qui a pour but de convertir l’affichage du calcul brut en un temps de type 00:00dans laquelle j’applique ta méthode ne fonctionne pas car il semblerai que je ne puisse par mettre autre chose que des « let » sans avoir un message d’erreur.
Du coup, j’ai mis les lignes de codes à la suite des calculs pour voir et j’ai aussi le problème:

Tu as répondu trop vite … Regarde à nouveau mon post. J’avais utilisé formTruncatingRemainder() dans une première version, puis corrigé tout de suite avec truncatingRemainder(), qui ne pose pas ce problème.

EDIT : j’aurais dus mettre un EDIT dans le post, pour signaler le changement de syntaxe.

effectivement, désolé. Je suis un peu au taqué et je bosse dessus tous les jours et dès que j’ai vu, j’ai testé!
Ça fonctionne. Il ne me reste plus qu’à revoir les % car j’ai quand même des écarts de secondes qui s’amplifient en fonction des distances.

Avant de passer au cours des bases de données de Maxime, ça me fait bien manipuler les variables et les fonctions. Et j’apprends des trucs sur les conversions de temps dont je vais avoir besoin très souvent pour mon job. Merci!

Je vais surement revenir dès que je vais réintégrer le slider!