Arrondir un double à  2 chiffres après la virgule

Bonjour à  tous


 


Mon sujet peut paraitre ridicule mais je cherche à  arrondir un double, mais en vain. J'ai essayé 3 méthodes qui ne marchent pas : 



double A = valFloat.doubleValue; //issue de la récupération d'un CTTime d'un currenteTime d'un NSPlayerItem et qui donne 0.04000000000001
double B = 0.04000000000001;
double C = 0.07777777777779;

double testA = (round(A*100))/100;
double testB = (round(B*100))/100;
double testC = (round(C*100))/100;

NSString *test4 = [NSString stringWithFormat:@%.2f,A]; testA = [test4 doubleValue];
NSString *test5 = [NSString stringWithFormat:@%.2f,B]; testB = [test5 doubleValue];
NSString *test6 = [NSString stringWithFormat:@%.2f,C]; testC = [test6 doubleValue];

NSNumberFormatter *fmt = [[[NSNumberFormatter alloc] init]autorelease];
[fmt setMaximumFractionDigits:2];

test4 = [fmt stringFromNumber:[NSNumber numberWithFloat:A]]; testA = [test4 doubleValue];
test5 = [fmt stringFromNumber:[NSNumber numberWithFloat:B]]; testB = [test5 doubleValue];
test6 = [fmt stringFromNumber:[NSNumber numberWithFloat:C]]; testC = [test6 doubleValue];


Des idées pour obtenir respectivement dans un double 0.04, 0.04 et 0.08 ?


 


Dans l'attente de vous lire et merci d'avance


Réponses

  • Joanna CarterJoanna Carter Membre, Modérateur

    Dans un mot, impossible !


     


    Les chiffres "floating point" sont, par défaut, "indistincte"


     


    C"est seulement en les présentant comme strings que l'on puisse avoir la représentation exacte.


     


    Ou, tu peux bidouiller avec NSDecimalNumber, mais je ne vois pas ce que tu gagnerais.


  • Est-ce que ceci marche ?



    double testA = (floor(A*100+0.5))/100;
    double testB = (floor(B*100+0.5))/100;
    double testC = (floor(C*100+0.5))/100;


  •  


    Est-ce que ceci marche ?



    double testA = (floor(A*100+0.5))/100;
    double testB = (floor(B*100+0.5))/100;
    double testC = (floor(C*100+0.5))/100;



    J'aurais essayé la même chose, afin d'arrondir à  la valeur la plus proche.

  • Merci Draken pour ta réponse mais je n'ai pas mieux. Voici le résultat : 


     


    testA = (double) 0.04000000000000001


    testB = (double) 0.04000000000000001


     



    testC = (double) 0.08000000000000002


     


     


    Et pour répondre à  Joanna Carter le but de ma manoe“uvre est d'utiliser ces valeurs pour aller sur une image précise dans une séquence vidéo. Dans cet exemple, l'image 1 est à  0.04 seconde et si j'ai 0.04000000000000001, je passe à  l'image 2...d'où mon problème...

  • .... et je viens de tester les différents paramètres de NSDecimalNumber, en vain...


  • Il n'y a pas un type de données particulier pour la gestion du temps pour ce qui concerne la vidéo comme CMTime ?


  • En fait j'avais fait le choix de me déplacer via un slider en prenant comme référence les temps en seconde que j'avais pu récupérer en amont... Bon c'est vrai que j'avais de choix entre les temps en seconde ou les timeValue du CMTime...peut être que mon choix doit se porter plutôt sur les timeValues !!!!


    Je vais voir ce que ça donne dans ce sens


    Merci en tout cas Lexxis pour ta piste

  • Joanna CarterJoanna Carter Membre, Modérateur
    avril 2017 modifié #9

    C'est ce que j'allais dire. Pour créer un CMTime de 0,04 secs



    let time = CMTime(value: 4, timescale: 100)

    Mais, pour la vidéo, tu peux spécifier l'intervalle en nombres de frames :



    let time = CMTime(value: n, timescale: 25)

    let time = CMTime(value: n, timescale: 30)

    let time = CMTime(value: n, timescale: 60)

    ... où n représente le nombres de frames et 25/30/60 représente le frame rate


  • DrakenDraken Membre
    avril 2017 modifié #10

    On peut utiliser une variable intermédiaire pour l'arrondi.



    let a:Double = 0.0400000000000001
    let b:Double = a*100.0
    let c:Int64 = Int64(b)
    let d:Double = Double(c)/100.0
    print ("a : ", a)
    print ("b : ", b)
    print ("c : ", c)
    print ("d : ", d)



     


    a :  0.0400000000000001


    b :  4.00000000000001


    c :  4


    d :  0.04


     


  • Désolé Draken mais chez moi, d = 0.0400000000001 !!


     


    Donc je pense que la piste CMTime reste la meilleure 


     


    Je teste et je reviens vers vous


  • ???


    Comment peut-il rester une partie fractionnaire dans une variable de type Int ? Je ne vois pas pourquoi un code qui fonctionne en Swift ne le ferais pas en Objective-C. 


     


    On peut voir ton code ? tu dois utiliser une formulation compact que le compilateur optimise, en retirant des choses inutiles (à  ses yeux).

  • Je suis bien d'accord avec toi Draken et c'est bien ce qui m'agace...


    Quand à  mon code, tu l'as au début de ce sujet. Il est simplement placé dans -(void)awakeFromNib

  • Joanna CarterJoanna Carter Membre, Modérateur

    C'est la différence entre les processeurs, etc, pas le langage.


     


    Comme j'ai déjà  dit, il n'est jamais possible, de manière fiable, limiter le nombre de chiffres après la virgule.


  • CéroceCéroce Membre, Modérateur
    avril 2017 modifié #15

    Comment peut-il rester une partie fractionnaire dans une variable de type Int ?

    L'erreur ne provient pas du Int.
    C'est toi qui introduis l'erreur en divisant par 100, parce que 1/100 n'est pas codable par une somme de puissances de 2.

    https://fr.wikipedia.org/wiki/IEEE_754
    Les nombres sont toujours stockés en binaire, et le compilateur, la saisie et l'affichage convertissent les nombres décimaux en binaire.

    NSDecimalNumber n'a pas ces problèmes, mais il faudra tout de même convertir le NSDecimalNumber en float ou double à  un moment donné, alors ça ne règle rien.

    La seule bonne réponse est d'utiliser CMTime, qui permet de penser en "frames" plutôt qu'en secondes, et est donc calé précisément. Si ça n'a pas besoin d'être précis à  la frame, alors il faut tronquer les chiffres à  l'affichage, comme indiqué par Joanna dès sa première réponse.
  • DrakenDraken Membre
    avril 2017 modifié #16


    Désolé Draken mais chez moi, d = 0.0400000000001 !!


     




    Et cela ne risque pas de changer ..


    Tu utilises TROIS méthodes d'arrondi qui ne fonctionnent pas.


    Je t'en propose une QUATRIEME, utilisant une variable intermédiaire de type Int, qui elle fonctionne .


    Tu ne l'implémentes pas, tu ne la testes pas.. Tu réutilises ton ancien code et .. oh surprise .. toujours la même erreur ! Et tu en déduis que la méthode 4 ne fonctionne pas..  ???


  • Joanna CarterJoanna Carter Membre, Modérateur

    Moi, je l'ai testé et ça ne plane pas ici. Le moment que l'on utilise un Double ou un Float, les erreurs d'arrondissage arrivent.


     


    Je suis d'accord de Céroce - il faut utiliser le CMTime.


  • DrakenDraken Membre
    avril 2017 modifié #18


    L'erreur ne provient pas du Int.

    C'est toi qui introduis l'erreur en divisant par 100, parce que 1/100 n'est pas codable par une somme de puissances de 2.

     




    Bah non, je n'introduis pas d'erreur, puisque le nombre a déjà  été arrondis.


     


     


    Ceci dis :



    // Un Double stocke une valeur flottante avec 15 chiffres significatifs
    // Double.pi est une valeur arrondie de pi à  15 chiffres significatifs
    let p1:Double = Double.pi
    let p2:Double = p1/100
    let p3:Double = p2*100
    print (p1, "(valeur)")
    print (p2, "(valeur/100)")
    print (p3, "(valeur/100, puis x100")
     


     


    3.14159265358979 (valeur)


    0.0314159265358979 (valeur/100)


    3.14159265358979 (valeur/100, puis x100)


     


     

     


     





    func arrondi(valeur:Double) -> Double {
    let b:Double = valeur*100
    let intermediaire:Int64 = Int64(b)
    return Double(intermediaire)/100
    }



    let p1:Double = Double.pi
    let p2:Double = p1/100
    let p3:Double = p2*100
    let p4:Double = arrondi(valeur: p1)
    print (p1, "(valeur)")
    print (p2, "(valeur/100)")
    print (p3, "(valeur/100, puis x100)")
    print (p4, "(valeur tronquée à  2 chiffres aprés la virgule")



     


    3.14159265358979 (valeur)


    0.0314159265358979 (valeur/100)


    3.14159265358979 (valeur/100, puis x100)


    3.14 (valeur tronquée à  2 chiffres aprés la virgule


     


     


     

    EDIT : Petite correction, j'avais inversé une multiplication et une division dans le code !
  • Tout d'abord pout Draken, en effet, si je fais un NSLog, j'ai bien 0.04 qui s'affiche en revanche, si je place un point d'arrêt et que je lis c, j'ai 0.0400000000001.


    Par contre compte tenu des arguments de Céroce, je comprends donc mieux la raison de cela et je le rejoins ainsi que Joanna Carter qu'il faut que je travaille plutôt sur CMTime


     


    Merci à  tous en tout cas

  • Salut , 


     


    Voilà  ce que j'ai réussi a faire, bon c'est une façon " a l'arache" mais sa a l'air de marcher, par contre il faudra que tu modifie le code pour éviter les craches car je n'ai pas tester toutes les possiblités. 



    let x:Double = 454/55
    let numberToString = "\(x)"
    let z = numberToString as NSString
    let needle: Character = "."
    if let idx = numberToString.characters.index(of: needle) {
    let pos = numberToString.characters.distance(from: numberToString.startIndex, to: idx)
    let intPos = Int(pos)
    let lenght = intPos + 3

    let newString = z.substring(with: NSRange(location:0, length: lenght))
    if let chiffreFinal = Double(newString){
    print("\(chiffreFinal)")
    }
    }
  • Joanna CarterJoanna Carter Membre, Modérateur
    Mais tous ce code dépend sur la conversion d'un string vers un double et tu utilises un point pour le caractère de séparation ; ce qui ne va pas sur un ordi français.


    Non, le clé, c'est que NSLog utilise un formatage différent que le débogguer.


    Mais, comme dit Fred20, ce n'est pas l'apparence des chiffres qui compte, c'est la valeur en-dessous, qui ne peut jamais être fiable à  deux chiffres après la virgule.
Connectez-vous ou Inscrivez-vous pour répondre.