Récupérer le nombre de chiffres après la virgule d'un nombre décimal

Bonjour,

Je travaille avec une nano 33 - 32 bits - avec des variables de type double.
j'effectue des calculs dont j'affiche le résultat arrondi à 7 chiffres après la virgule.
Lorsque le résultat présente un nombre décimal dont le nombre de chiffres est inférieur ou égal à 7 je souhaite récupérer le nombre de chiffres après la virgule.

Par exemple si je divise 45.256 par 2, le résultat est 22.6280000. Je voudrai pouvoir récupérer le nombre de chiffres après la virgule, c'est à dire 3.

Savez-vous si il existe une fonction pour faire ça ?

Merci par avance.

il n'existe pas de fonction à ma connaissance mais vous pouvez l'écrire. vous convertissez votre double en chaîne (fonction dtostrf()) avec 7 chiffres après la Virgule puis vous comptez le nombre de 0 ininterrompus depuis la fin de la chaîne (on connait l'index avec strlen() - 1) jusqu'au point décimal

Merci JML,

J'ai trouver que sur la nano 33 il fallait : #include <avr/dtostrf.h>

Je pense que c'est ça que vous évoquez :

#include <avr/dtostrf.h>

const double MonChiffre = 45.25 ;
char z[20];
byte nb = 0;
byte NombreChiffreApresVirgule = 0;

void setup() {
  
Serial.begin(57600);
  

}

void loop() {
 //Serial.println(MonChiffre,7);
 
 dtostrf(MonChiffre,6,7,z);
 for (byte f = 0; f <= (strlen(z)); f++)
 {
  if (z[f] =='0') nb = nb + 1;
 }
 
 NombreChiffreApresVirgule = 7 - nb;
 Serial.println(NombreChiffreApresVirgule);
 nb=0;
 NombreChiffreApresVirgule = 0;
 delay(5000);
}

La variable NombreChiffreApresVirgule m'indique bien ce que je voulais.

Par contre, je ne comprends pas trop bien à quoi sert le deuxième paramètre de la fonction dtostrf (j'ai mis 6 au hasard), il semble que ce soit pour définir la largeur de la variable de sortie ou le nombre de chiffres mais c'est un paramètre que je ne peux pas connaitre à l'avance.

les paramètres c'est

dtostrf(valeur, largeur_min, nb_chiffres_après_la_virgule, buffer)

  • largeur min, mettez 0 (sinon ça rajoute des espaces si vous avez un petit nombre)
  • nb_chiffres_après_la_virgule c'est 7
  • il faut que le buffer soit assez grand pour votre plus grand nombre et avec une case en plus pour le caractère nul à la fin

ensuite votre fonction ne va pas bien, vous comptez le nombre de '0' mais si mon nombre c'est 100.0001000 vous voyez bien que ça ne va pas fonctionner (et si la longueur donnée par strlen c'est 5 il faut lire les caractère jusqu'à 4 au plus, pas prendre le caractère à l'indice 5 - ce sera le '\0' qui marque la fin de chaîne)

il faut partir de la droite et compter le nombre de 0 jusqu'à ce que vous rencontriez quelque chose de différent de '0' (vous tomberez sur le '.' point décimal ou un chiffre)

J'essaye avec ce code mais je ne comprends pas car nb ne s'incrémente pas :

#include <avr/dtostrf.h>

const double MonChiffre = 45.25 ;
char z[20];
byte nb = 0;
byte NombreChiffreApresVirgule = 0;

void setup() {
  
Serial.begin(57600);
  

}

void loop() {
 //Serial.println(MonChiffre,7);
 
 dtostrf(MonChiffre,0,7,z);
 for (byte f = (strlen(z)); f = 0; f--)
 {
 if (z[f] =='0')  nb = nb + 1;
 if (z[f] !='0') break;
 }
 
 NombreChiffreApresVirgule = 7 - nb;
 Serial.println(nb);
 Serial.println(NombreChiffreApresVirgule);
 nb=0;
 NombreChiffreApresVirgule = 0;
 delay(5000);
}

Pareil pour ce code :

#include <avr/dtostrf.h>

const double MonChiffre = 45.25 ;
char z[20];
byte nb = 0;
byte NombreChiffreApresVirgule = 0;

void setup() {
  
Serial.begin(57600);
  

}

void loop() {
 //Serial.println(MonChiffre,7);
 
 dtostrf(MonChiffre,0,7,z);
 for (byte f = (strlen(z)); f = 0; f--)
 {
 while (z[f] =='0')  nb = nb + 1;
 //if (z[f] !='0') break;
 }
 
 NombreChiffreApresVirgule = 7 - nb;
 Serial.println(nb);
 Serial.println(NombreChiffreApresVirgule);
 nb=0;
 NombreChiffreApresVirgule = 0;
 delay(5000);
}

Regardez la condition (== c’est pas comme =).

(Attention aussi au fait qu’un byte c’est non signé donc si vous aviez envie de tester <0, ça n’irait pas)

Vous voulez commencer à l’indice strlen - 1 qui est ke dernier caractère

Oui effectivement JML mon tableau de char a 20 éléments de 0 à 19 donc :

for ( int f = (strlen(z)-1); f = 0; f--)

où f est une variable de type int

Là j'arrive pas à comprendre puisque dans une boucle for on ne met pas == mais = ?
De plus si je mets == le compilateur indique une erreur ?

merci

Bon, je dois aller au restaurant et demain matin je me lève à 04h00 pour essayer de photographier le brame du cerf...
Je m'y remettrai demain d'autant plus que madame trouve que je passe trop de temps sur mon ordinateur.

Merci JML.
bonne soirée.

La condition est ici

Pour la condition (cond-expression)

for ( init-expression ; cond-expression ; loop-expression )
 statement

Comme ça c'est bon ?

dtostrf(MonChiffre,0,7,z);
 
 for ( int f = (strlen(z)-1); f >= 0; f--)
  {
  if (z[f] == '0')  
 {
  nb = nb + 1;
 }
 else
 {
 break;
 }
NombreChiffreApresVirgule = 7 - nb;

Pour moi, j'ai bien le bon nombre de chiffres après la virgule. J'ai bien ce que je voulais.

Dans une boucle for, le deuxième paramètre fixe la limite de l'itération mais lorsque j'utilise :

 for ( int f = (strlen(z)-1); f == 0; f--)

il n'y a pas d'itération si bien que ma variable "n" n'est pas incrémentée, c'est pour cette raison que je ne comprenais pas hier où vous vouliez m'amener ?

Est-ce que je peux mieux faire ou c'est bon ?
Ensuite je vais essayer de créer une fonction qui retournera le nombre de chiffre après la virgule et si vous le voulez bien, je vous la soumettrai.

En tout cas merci beaucoup pour votre attention.

Pour la fonction, j'ai fait ça :

#include <avr/dtostrf.h>

const double MonChiffre = 45.2314 ;
char z[20];

void setup() {
 Serial.begin(57600);
}

void loop() {

Serial.println(NombreChiffreAVir(MonChiffre,z));
delay (5000);
  
}

int NombreChiffreAVir ( double ChiffreDec, char * MonTableau) {
  int NombreChiffreApresVirgule = 0;
  int nb = 0;
  
  dtostrf(MonChiffre,0,7,z);
  for ( int f = (strlen(z)-1); f >= 0; f--) {
  
  if (z[f] == '0')  
 {
  nb = nb + 1;
 }
 else
 {
 break;
 }
 }
 NombreChiffreApresVirgule = 7 - nb;
 return NombreChiffreApresVirgule;
}

Elle fonctionne mais est-ce qu'il y a mieux à faire ?

Merci beaucoup par avance.
Philippe.

Non.

for ( init-expression ; cond-expression ; loop-expression )
statement

  • init-expression, expression appliquée lors de l'entrée dans la boucle
  • cond-expression, condition de maintient dans la boucle. Tant que l'expression est vrai on exécute la boucle
  • loop-expression, expression exécutée à la fin de chaque boucle

MonTableau ne sert à rien, vous utilisez le buffer global. ce buffer pourrait être local à la fonction
vous pourriez durcir un peu la fonction, vous assurer qu'il y a bien un point par exemple dans le buffer (fonction strchr()) (il devrait toujours être là mais à vérifier car je pense que même pour un nombre entier comme 0.0 dtostrf() donnera "0.0000000" et pas juste "0")

Merci fdufnews, c'est sûr que si f = 0, la boucle ne peut s'effectuer. Et merci aussi à JML et lesept.

JML c'est mieux comme ça ?

#include <avr/dtostrf.h>

const double MonChiffre = 45.2568 ;


void setup() {
 Serial.begin(57600);
}

void loop() {

Serial.println(NombreChiffreAVir(MonChiffre));
delay (5000);
  
}

int NombreChiffreAVir (double ChiffreDec) {
  int NombreChiffreApresVirgule = 0;
  int nb = 0;
  char z[20];
  char *recherchePoint = strchr(dtostrf(MonChiffre,0,7,z),'.');

  if (recherchePoint !=NULL)  Serial.println("un point est présent")
  
  dtostrf(MonChiffre,0,7,z);
  for ( int f = (strlen(z)-1); f >= 0; f--) {
  
  if (z[f] == '0')  
 {
  nb = nb + 1;
 }
 else
 {
 break;
 }
 }
 NombreChiffreApresVirgule = 7 - nb;
 return NombreChiffreApresVirgule;
}

Pour le point, j'ai réussi à utiliser la fonction strchr() que je ne connaissais pas mais en principe dans mon programme 0 sera égal à 0.0000000.

Merci à tous les trois.

Oupsssssssss
j'ai pas donné le bon code, le voici :

#include <avr/dtostrf.h>
void setup() {
 Serial.begin(57600);
}

void loop() {

Serial.println(NombreChiffreAVir(45.256844));
delay (5000);
  
}

  int NombreChiffreAVir (double ChiffreDec) {
  int NombreChiffreApresVirgule = 0;
  int nb = 0;
  char z[20];
  char *recherchePoint = strchr(dtostrf(ChiffreDec,0,7,z),'.');

  if (recherchePoint !=NULL)  Serial.println("un point est présent");
  
  dtostrf(ChiffreDec,0,7,z);
  for ( int f = (strlen(z)-1); f >= 0; f--) {
  
  if (z[f] == '0')  
 {
  nb = nb + 1;
 }
 else
 {
 break;
 }
 }
 NombreChiffreApresVirgule = 7 - nb;
 return NombreChiffreApresVirgule;
}

on peut un peu simplifier (pas la peine d'appeler deux fois dtostrf) mais c'est cela

int NombreChiffreAVir (double ChiffreDec) {
  int nb = 0;
  char tmpBuffer[20];

  char *recherchePoint = strchr(dtostrf(ChiffreDec, 0, 7, tmpBuffer), '.');
  if (recherchePoint == nullptr) return 0;

  for (int f = strlen(tmpBuffer) - 1; f >= 0; --f)
    if (tmpBuffer[f] == '0') nb++;
    else break;
  return 7 - nb;
}

faudra vous poser la question si 20 c'est suffisant pour le buffer

Merci beaucoup JML.
Bonne soirée à vous.

JML,

Décidément vous inspirez le respect. j'admire vos compétences tant au niveau technique que de la programmation.
Cette fonction fonctionne parfaitement. Je la comprends mais j'ai quelques questions :

  • Il y a une différence dans la boucle for entre --f et f-- ?
  • je constate que dans une boucle for, on peut ne pas mettre les instructions à exécuter entre : {}, peut-être parce que l'on termine par : else break; ?
  • char *recherchePoint = strchr(dtostrf(ChiffreDec, 0, 7, tmpBuffer), '.'); if (recherchePoint == nullptr) return 0; ici *recherchePoint recherche un pointeur, une adresse en mémoire qui contient un point ? du coup si il n'y a pas de pointeur, d'adresse : la valeur du pointeur est nulle d'où le mot clé nullptr ?

Merci.