1
votes

Comment est la méthode standardisée pour calculer le flottant avec des entiers?

Est-ce que l'un de vous sait comment cela sera calculé en C?

uint8_t samplerate = 200;
uint8_t Result;
Result = 0.5 * samplerate;

Maintenant, le problème est que 0,5 est un flottant et échantillonner un entier. Result pourrait alors être soit 0, car 0.5 est converti en un entier et donc arrondi à 0 ( Result = 0 * 200 = 0 ). Ou Result pourrait être 100, car le compilateur voit d'abord 0.5 et convertit samplerate en float ( Result = 0.5 * 200 = 100 ). p>

Existe-t-il une manière standardisée de gérer ces calculs par le compilateur? Je veux dire que le compilateur examinera d'abord la variable à l'extrême gauche (dans ce cas 0,5) et convertira l'autre en ceci, ou examinera-t-il la variable à l'extrême droite ( samplerate ) et convertira les autres variables à cela?

Je sais comment je pourrais résoudre ce problème mais je cherche une réponse générale, si c'est C standardisé et comment calculera-t-il de telles équations?


5 commentaires

Veuillez lire cette référence de conversion implicite . Il vous dira que 0.5 ne sera pas converti en un entier, mais au contraire, samplerate sera converti en un double .


Règles de promotion de type implicite


@Someprogrammerdude, j'ai bien peur que vous n'utilisiez comme référence une référence C ++ avec ses règles de conversion (différentes). En raison de l'existence possible d'un constructeur de paramètre, les règles de conversion implicite automatique en C ++ sont beaucoup plus complexes (elles sont dynamiques, au moment de l'exécution) qu'elles ne le sont en C et l'exemple que vous avez publié n'est pas valide.


@LuisColorado Ce site peut être appelé "cppreference", mais le site contient des références pour les deux C et C ++ . Le lien que j'ai fourni va vers la référence de conversion C


@Someprogrammerdude, mes excuses pour mon erreur. Tu avais raison.


3 Réponses :


4
votes

Oui, bien sûr, cela est contrôlé par la norme, il n'y a pas d'incertitude ici.

Fondamentalement, l'entier sera promu en double (puisque le type de 0.5 est double , ce n'est pas float ) et le calcul s'y déroulera, alors le résultat sera tronqué à uint8_t . Le compilateur vous criera pour la perte de précision, généralement. Si ce n'est pas le cas, ajoutez d'autres options d'avertissement si nécessaire.


2 commentaires

Notez également que le comportement lors de la conversion d'un double inférieur ou égal à -1 en un type non signé n'est pas défini.


mais le comportement ici n'est pas de convertir un double qui est inférieur à -1, c'est bien 100.0 , et permet même une représentation exacte en cas de représentation interne IEEE-752 de double s. Quelle est la raison de votre commentaire? le résultat double ici est 100.0 donc il peut être converti en unsigned et rentre dans sa représentation (il est inférieur à 255.0 )



0
votes

Oui, il existe une norme. Dans ce cas, les nombres de l'expression sont automatiquement convertis dans le type le plus large (celui qui occupe plus d'octets), donc votre expression sera évaluée comme suit:

(0.5: double) * (0: uint8_t) => (0.5: double) * (0.0: double) == (0.0: double)
uint8_t Result = (0.0: double) => (0: uint8_t) // this is a forced cast, because Result is of type uint8_t

double code> est plus large que uint8_t , donc (0: uint8_t) est élargi à (0.0: double) . Ce casting ne perd pas d'informations puisque double occupe suffisamment d'espace pour contenir toutes les données stockées dans uint8_t.


13 commentaires

Pour être pointilleux, long long occupe probablement plus d'octets que float , mais il sera converti en float si l'autre opérande est de ce type.


Qualifier cela de problème d'élargissement est à la fois trompeur et incomplet. Dans ce cas, le uint8_t est converti en double pour le calcul car le type réel correspondant de l'autre opérande est double , ni plus, ni moins. Si l'opérande flottant était 0.5f , un float , et l'autre était un long long , alors ce dernier serait converti en float pour l'opération, même si long long est probablement plus large que float (et qu'il soit ou non plus large).


@Lundin, pour être pointilleux, long long occupe probablement au moins autant d'octets que long . Cela n'implique pas que long long soit nécessairement supérieur à float . Peut être de la même taille ou même plus courte. J'ai peur que vous ne soyez pas pointilleux, mais que vous vous trompiez.


@LuisColorado Sur à peu près toutes les implémentations du monde réel connues, long long fait 8 octets et float 4 octets. Pourtant, longtemps sera "promu" pour flotter.


@Lundin, les promotions en C à partir de valeurs entières (n'importe quelle taille, même char ) vont aux valeurs double . J'ai bien peur qu'on vous en ait déjà parlé. Quoi qu'il en soit, les promotions de long long perdent toujours des informations, car long long ont 64 bits significatifs, tandis que double n'en a que 52. Quel est le problème? Je pense que vous insistez pour rechercher un nombre à virgule flottante plus large ... et ce n'est pas le problème. La norme dit que les entiers mélangés à la virgule flottante passent à la virgule flottante, même si cela signifie perdre une certaine précision (et vous le faites presque toujours)


Les float n'ont d'ailleurs que 24 bits pour le significand ... même un int 32 bits perd en précision lorsqu'il est promu en float . Vous devez vérifier votre idée. Normalement, vous n'essayez pas un nombre de précision plus large ... les règles sont fixes. L'autre type d'opérande est ce qui impose le type de conversion, comme John Bollinger vous l'a dit dans un autre commentaire.


Les implémentations ne fixent pas le standard ... c'est juste le contraire: le standard impose ce que font les implémentations. La seule exigence pour long long est d'être au moins plus large ou égal à long , car long doit être au moins plus large que int pour qu'ils soient tous égaux. Rien n'empêche donc long long d'être 16 bits dans une implémentation conforme. Vivez avec cela à l'esprit.


Eh bien, je pense que je me suis extralimité. Certains standards font de long au moins 32 bits .... donc long long doit avoir au moins cette taille. Mes excuses pour cela.


@LuisColorado "les promotions en C à partir de valeurs entières (n'importe quelle taille, même char) vont aux valeurs doubles" Non. Veuillez étudier "les conversions arithmétiques usuelles", C17 6.3.1.8. Sinon, si le type réel correspondant de l'un des opérandes est float, l'autre opérande est converti, sans changement de type domain, en un type dont le type réel correspondant est float. . Vous vous trompez également sur la taille des types, long long doit être d'au moins 63 bits pour être conforme. C17 5.2.4.2.1 / 1 Tailles des types entiers "valeur minimale pour un objet de type long long int -9223372036854775807 // - (2 ^ 63 - 1)"


@Lundin, veuillez considérer aller aux valeurs doubles comme une faute de frappe. Je connais les conversions arithmétiques habituelles. Ce qui ne va pas, c'est de considérer (comme vous le faites dans votre commentaire) que la chose est d'arriver à un type de précision plus large, c'est faux. Si un autre opérande est plus étroit, il y a une perte de précision. Pour les tailles réelles des nombres entiers, je suis dans le monde C depuis le système Unix III, et j'ai bien peur d'avoir vu beaucoup trop de tailles d'entiers dans ma vie pour m'en souvenir toutes. Je ne suppose normalement pas de taille minimale ou maximale pour un entier générique, mais considérez comme le standard l'a fait pendant longtemps que ...


... long est au moins plus grand (ou égal, n'oubliez jamais) que int et long long pour être au moins plus grand (ou égal) que long . J'ai vécu des systèmes avec int étant 16 bits, long étant 32 et long long n'existaient même pas du tout. Ne me plaignez pas de cela, car je me souviens d'autant de types entiers qu'il est difficile d'énumérer de quelle version standard vous parlez.


@LuisColorado int devait toujours être d'au moins 15 bits + signe et long devait toujours être d'au moins 31 bits + signe. Depuis l'aube de C et Unix - cela n'a jamais été changé. long long a été introduit il y a 20 ans avec la norme C99. Quant à la précision, je ne l'ai mentionnée dans aucun de mes commentaires.


@Lundin, alors je vous ai mal compris. Mes excuses pour cela. Mon observation était seulement que vous avez dit que être difficile long long occupe probablement plus d'octets que float et ce n'est pas nécessairement vrai.



4
votes

Lorsque des valeurs numériques de différents types sont combinées dans une expression, elles sont soumises aux conversions arithmétiques habituelles , qui sont un ensemble de règles qui déterminent quel opérande doit être converti et dans quel type.

/ p>

Ces conversions sont décrites dans la section 6.3.1.8 du standard C:

De nombreux opérateurs qui attendent des opérandes de type arithmétique provoquent conversions et donnent des types de résultats de la même manière. Le but est pour déterminer un type réel commun pour les opérandes et le résultat. Pour le opérandes spécifiés, chaque opérande est converti, sans changement de type domaine, à un type dont le type réel correspondant est le type réel commun. Sauf indication contraire explicite, le le type réel commun est également le type réel correspondant du result, dont le domaine de type est le domaine de type des opérandes si elles sont identiques et complexes autrement. Ce modèle est appelé les conversions arithmétiques habituelles:

  • Premièrement, si le type réel correspondant de l'un des opérandes est long double, l'autre opérande est converti, sans changement de domaine de type, en un type dont le type réel correspondant est long double.
  • Sinon, si le type réel correspondant de l'un des opérandes est double, l'autre opérande est converti, sans changement de domaine de type, en un type dont le type réel correspondant est double.
  • Sinon, si le type réel correspondant de l'un des opérandes est float, l'autre opérande est converti, sans changement de domaine de type, en un type dont le type réel correspondant est flotter.
  • Sinon, les promotions d'entiers sont effectuées sur les deux opérandes. Ensuite, les règles suivantes sont appliquées à la promotion opérandes:
    • Si les deux opérandes ont le même type, alors pas plus une conversion est nécessaire.
    • Sinon, si les deux opérandes sont signés les types entiers ou les deux ont des types entiers non signés, l'opérande avec le type de rang de conversion entier inférieur est converti au type de l'opérande de rang supérieur.
    • Sinon, si le l'opérande qui a un type entier non signé a un rang supérieur ou égal au rang du type de l'autre opérande, alors l'opérande de type entier signé est converti en type de l'opérande de type entier non signé.
    • Sinon, si le le type de l'opérande avec le type entier signé peut représenter tous les valeurs du type de l'opérande de type entier non signé, puis le l'opérande de type entier non signé est converti en type de l'opérande de type entier signé.
    • Sinon, les deux opérandes sont convertis en type entier non signé correspondant au type de l'opérande de type entier signé.

Notez en particulier le paragraphe en gras, ce qui s'applique dans votre cas.

La constante à virgule flottante 0.5 est de type double , donc la valeur de l'autre opérande est convertie en type double , et le résultat du l'opérateur de multiplication * est de type double . Ce résultat est ensuite réaffecté à une variable de type uint8_t , de sorte que la valeur double est convertie dans ce type pour l'affectation.

Dans ce cas, Result aura la valeur 100.


0 commentaires