Partager via


Unités de mesure

Les valeurs entières à virgule flottante et signées en F# peuvent avoir des unités de mesure associées, qui sont généralement utilisées pour indiquer la longueur, le volume, la masse, et ainsi de suite. En utilisant des quantités avec des unités, vous autorisez le compilateur à vérifier que les relations arithmétiques ont les unités correctes, ce qui permet d’éviter les erreurs de programmation.

Remarque

Ces exemples illustrent l’exactitude dans les calculs arithmétiques impliquant des unités de mesure, la fonctionnalité peut également être exploitée pour ajouter une annotation sécurisée de type avec zéro coût de représentation à d’autres types, avec une approche telle que le projet FSharp.UMX .

Syntaxe

[<Measure>] type unit-name [ = measure ]

Remarques

La syntaxe précédente définit le nom d’unité comme unité de mesure. La partie facultative est utilisée pour définir une nouvelle mesure en termes d’unités précédemment définies. Par exemple, la ligne suivante définit la mesure cm (centimètre).

[<Measure>] type cm

La ligne suivante définit la mesure ml (millilittères) comme un centimètre cube (cm^3).

[<Measure>] type ml = cm^3

Dans la syntaxe précédente, la mesure est une formule qui implique des unités. Dans les formules qui impliquent des unités, les puissances intégrales sont prises en charge (positives et négatives), les espaces entre les unités indiquent un produit des deux unités, * indique également un produit d’unités et / indique un quotient d’unités. Pour une unité réciproque, vous pouvez utiliser une puissance entière négative ou une / séparation entre le numérateur et le dénominateur d’une formule d’unité. Plusieurs unités dans le dénominateur doivent être entourées de parenthèses. Les unités séparées par les espaces après qu’un élément / sont interprétées comme faisant partie du dénominateur, mais toutes les unités suivantes * sont interprétées comme faisant partie du numérateur.

Vous pouvez utiliser 1 dans les expressions d’unité, soit seule pour indiquer une quantité sans dimension, soit avec d’autres unités, comme dans le numérateur. Par exemple, les unités d’un taux sont écrites en tant que 1/ssecondes s . Les parenthèses ne sont pas utilisées dans les formules d’unité. Vous ne spécifiez pas de constantes de conversion numériques dans les formules d’unité ; Toutefois, vous pouvez définir des constantes de conversion avec des unités séparément et les utiliser dans des calculs vérifiés par unité.

Les formules unitaires qui signifient que la même chose peut être écrite de différentes manières équivalentes. Par conséquent, le compilateur convertit les formules d’unité en une forme cohérente, qui convertit les pouvoirs négatifs en puissances réciproques, regroupe les unités en un seul numérateur et un dénominateur, et alphabetise les unités dans le numérateur et le dénominateur.

Par exemple, les formules d’unité et m /s s * kg sont toutes deux converties kg m s^-2 en kg m/s^2.

Vous utilisez des unités de mesure dans des expressions à virgule flottante. L’utilisation de nombres à virgule flottante avec des unités de mesure associées ajoute un autre niveau de sécurité de type et permet d’éviter les erreurs d’incompatibilité d’unité qui peuvent se produire dans les formules lorsque vous utilisez des nombres à virgule flottante faiblement typés. Si vous écrivez une expression à virgule flottante qui utilise des unités, les unités de l’expression doivent correspondre.

Vous pouvez annoter des littéraux avec une formule d’unité entre crochets, comme illustré dans les exemples suivants.

1.0<cm>
55.0<miles/hour>

Vous ne placez pas d’espace entre le nombre et le crochet angle ; Toutefois, vous pouvez inclure un suffixe littéral tel que f, comme dans l’exemple suivant.

// The f indicates single-precision floating point.
55.0f<miles/hour>

Une telle annotation modifie le type du littéral de son type primitif (par exemple float) en un type dimensionné, tel que float<cm> ou, dans ce cas, float<miles/hour>. Une annotation d’unité indiquant <1> une quantité sans dimension et son type équivaut au type primitif sans paramètre d’unité.

Le type d’une unité de mesure est un type intégral à virgule flottante ou signé avec une annotation d’unité supplémentaire, indiquée entre crochets. Ainsi, lorsque vous écrivez le type d’une conversion de g (grammes) en kg (kilogrammes), vous décrivez les types comme suit.

let convertg2kg (x : float<g>) = x / 1000.0<g/kg>

Les unités de mesure sont utilisées pour la vérification de l’unité de compilation, mais elles ne sont pas conservées dans l’environnement d’exécution. Par conséquent, ils n’affectent pas les performances.

Les unités de mesure peuvent être appliquées à n’importe quel type, pas seulement aux types à virgule flottante ; toutefois, seuls les types à virgule flottante, les types intégraux signés et les types décimaux prennent en charge les quantités dimensionnée. Par conséquent, il est logique d’utiliser des unités de mesure sur les types primitifs et sur les agrégats qui contiennent ces types primitifs.

L’exemple suivant illustre l’utilisation d’unités de mesure.

// Mass, grams.
[<Measure>] type g
// Mass, kilograms.
[<Measure>] type kg
// Weight, pounds.
[<Measure>] type lb

// Distance, meters.
[<Measure>] type m
// Distance, cm
[<Measure>] type cm

// Distance, inches.
[<Measure>] type inch
// Distance, feet
[<Measure>] type ft

// Time, seconds.
[<Measure>] type s

// Force, Newtons.
[<Measure>] type N = kg m / s^2

// Pressure, bar.
[<Measure>] type bar
// Pressure, Pascals
[<Measure>] type Pa = N / m^2

// Volume, milliliters.
[<Measure>] type ml
// Volume, liters.
[<Measure>] type L

// Define conversion constants.
let gramsPerKilogram : float<g kg^-1> = 1000.0<g/kg>
let cmPerMeter : float<cm/m> = 100.0<cm/m>
let cmPerInch : float<cm/inch> = 2.54<cm/inch>

let mlPerCubicCentimeter : float<ml/cm^3> = 1.0<ml/cm^3>
let mlPerLiter : float<ml/L> = 1000.0<ml/L>

// Define conversion functions.
let convertGramsToKilograms (x : float<g>) = x / gramsPerKilogram
let convertCentimetersToInches (x : float<cm>) = x / cmPerInch

L’exemple de code suivant montre comment convertir un nombre à virgule flottante sans dimension en une valeur à virgule flottante dimensionnée. Vous venez de multiplier par 1,0, en appliquant les dimensions à la version 1.0. Vous pouvez l’abstractionr dans une fonction telle que degreesFahrenheit.

En outre, lorsque vous passez des valeurs dimensionnée à des fonctions qui attendent des nombres à virgule flottante sans dimension, vous devez annuler les unités ou les convertir à float l’aide de l’opérateur float . Dans cet exemple, vous divisez en 1.0<degC> fonction des arguments, printf car printf attend des quantités sans dimension.

[<Measure>] type degC // temperature, Celsius/Centigrade
[<Measure>] type degF // temperature, Fahrenheit

let convertCtoF ( temp : float<degC> ) = 9.0<degF> / 5.0<degC> * temp + 32.0<degF>
let convertFtoC ( temp: float<degF> ) = 5.0<degC> / 9.0<degF> * ( temp - 32.0<degF>)

// Define conversion functions from dimensionless floating point values.
let degreesFahrenheit temp = temp * 1.0<degF>
let degreesCelsius temp = temp * 1.0<degC>

printfn "Enter a temperature in degrees Fahrenheit."
let input = System.Console.ReadLine()
let parsedOk, floatValue = System.Double.TryParse(input)
if parsedOk
   then
      printfn "That temperature in Celsius is %8.2f degrees C." ((convertFtoC (degreesFahrenheit floatValue))/(1.0<degC>))
   else
      printfn "Error parsing input."

L’exemple de session suivant montre les sorties et entrées de ce code.

Enter a temperature in degrees Fahrenheit.
90
That temperature in degrees Celsius is    32.22.

Types primitifs prenant en charge les unités de mesure

Les types ou alias d’abréviation de type suivants prennent en charge les annotations unitaires de mesure :

Alias F# Type CLR
float32/single System.Single
float/double System.Double
decimal System.Decimal
sbyte/int8 System.SByte
int16 System.Int16
int/int32 System.Int32
int64 System.Int64
byte/uint8 System.Byte
uint16 System.UInt16
uint/uint32 System.UInt32
uint64 System.UIn64
nativeint System.IntPtr
unativeint System.UIntPtr

Par exemple, vous pouvez annoter un entier non signé comme suit :

[<Measure>]
type days

let better_age = 3u<days>

L’ajout de types entiers non signés à cette fonctionnalité est documenté dans F# RFC FS-1091.

Unités de mesure prédéfinies

Une bibliothèque d’unités est disponible dans l’espace FSharp.Data.UnitSystems.SI de noms. Il inclut des unités SI dans leur forme de symbole (comme m pour le compteur) dans l’espace UnitSymbols de sous-noms et dans leur nom complet (comme meter pour le compteur) dans l’espace UnitNames de sous-nom.

Utilisation d’unités génériques

Vous pouvez écrire des fonctions génériques qui fonctionnent sur des données qui ont une unité de mesure associée. Pour ce faire, spécifiez un type avec une unité générique comme paramètre de type, comme illustré dans l’exemple de code suivant.

// Distance, meters.
[<Measure>] type m
// Time, seconds.
[<Measure>] type s

let genericSumUnits ( x : float<'u>) (y: float<'u>) = x + y

let v1 = 3.1<m/s>
let v2 = 2.7<m/s>
let x1 = 1.2<m>
let t1 = 1.0<s>

// OK: a function that has unit consistency checking.
let result1 = genericSumUnits v1 v2
// Error reported: mismatched units.
// Uncomment to see error.
// let result2 = genericSumUnits v1 x1

Création de types de collection avec des unités génériques

Le code suivant montre comment créer un type d’agrégation qui se compose de valeurs à virgule flottante individuelles qui ont des unités génériques. Cela permet de créer un type unique qui fonctionne avec une variété d’unités. En outre, les unités génériques conservent la sécurité des types en garantissant qu’un type générique qui a un ensemble d’unités est un type différent du même type générique avec un ensemble différent d’unités. La base de cette technique est que l’attribut Measure peut être appliqué au paramètre de type.

 // Distance, meters.
[<Measure>] type m
// Time, seconds.
[<Measure>] type s

// Define a vector together with a measure type parameter.
// Note the attribute applied to the type parameter.
type vector3D<[<Measure>] 'u> = { x : float<'u>; y : float<'u>; z : float<'u>}

// Create instances that have two different measures.
// Create a position vector.
let xvec : vector3D<m> = { x = 0.0<m>; y = 0.0<m>; z = 0.0<m> }
// Create a velocity vector.
let v1vec : vector3D<m/s> = { x = 1.0<m/s>; y = -1.0<m/s>; z = 0.0<m/s> }

Unités au moment de l’exécution

Les unités de mesure sont utilisées pour la vérification de type statique. Lorsque les valeurs à virgule flottante sont compilées, les unités de mesure sont éliminées, de sorte que les unités sont perdues au moment de l’exécution. Par conséquent, toute tentative d’implémentation de fonctionnalités qui dépend de la vérification des unités au moment de l’exécution n’est pas possible. Par exemple, l’implémentation d’une ToString fonction pour imprimer les unités n’est pas possible.

Transformations

Pour convertir un type qui a des unités (par exemple) float<'u>en un type qui n’a pas d’unités, vous pouvez utiliser la fonction de conversion standard. Par exemple, vous pouvez utiliser float pour convertir en valeur float qui n’a pas d’unités, comme indiqué dans le code suivant.

[<Measure>]
type cm
let length = 12.0<cm>
let x = float length

Pour convertir une valeur sans unité en valeur qui a des unités, vous pouvez multiplier par une valeur de 1 ou 1,0 annotée avec les unités appropriées. Toutefois, pour écrire des couches d’interopérabilité, il existe également certaines fonctions explicites que vous pouvez utiliser pour convertir des valeurs sans unité en valeurs avec des unités. Il s’agit du module FSharp.Core.LanguagePrimitives . Par exemple, pour effectuer une conversion d’un unitless float en un float<cm>, utilisez FloatWithMeasure, comme illustré dans le code suivant.

open Microsoft.FSharp.Core
let height:float<cm> = LanguagePrimitives.FloatWithMeasure x

Voir aussi