Is there built in version of the type casting functions that preserves units and if not how would I make them? So for example with this code how would I cast intWithSecondsMeasure to a float without losing the measure or multiplying by 1.0<s>
?
[<Measure>] type s
let intWithSecondsMeasure = 1<s>
let justAFloat = float intWithSecondsMeasure
The answer provided by @kvb certainly works, but I'd prefer not to use the unbox
operator for this conversion. There's a better, built in way that I think should be compiled as a NOP to IL (I haven't checked but unbox is probably going to end up as a unbox
instruction in IL and thus adds a runtime type check).
The preferred way to do unit conversions in F# is LanguagePrimitives.TypeWithMeasure
(MSDN).
let inline float32toFloat (x:float32<'u>) : float<'u> =
x |> float |> LanguagePrimitives.FloatWithMeasure
I don't think that there's a built-in way to do it, but you can easily define your own unit-preserving conversion function:
let float_unit (x:int<'u>) : float<'u> = unbox float x
let floatWithSecondsMeasure = float_unit intWithSecondsMeasure
I compiled the code from kvb and Johannes answers.
Johannes answer
let float32toFloat (x:int<'u>) : float<'u> =
x |> float |> LanguagePrimitives.FloatWithMeasure
.method public static float64 float32toFloat(int32 x) cil managed
{
// Code size 3 (0x3)
.maxstack 8
IL_0000: ldarg.0
IL_0001: conv.r8
IL_0002: ret
} // end of method Program::float32toFloat
kvb answer with parentheses added.
let float_unit (x:int<'u>) : float<'u> = unbox (float x)
.method public static float64 float_unit(int32 x) cil managed
{
// Code size 13 (0xd)
.maxstack 8
IL_0000: ldarg.0
IL_0001: conv.r8
IL_0002: box [mscorlib]System.Double
IL_0007: unbox.any [mscorlib]System.Double
IL_000c: ret
} // end of method Program::float_unit
kvb answer
let float_unit (x:int<'u>) : float<'u> = unbox float x
.method public static float64 float_unit(int32 x) cil managed
{
// Code size 19 (0x13)
.maxstack 8
IL_0000: newobj instance void Program/float_unit@4::.ctor()
IL_0005: call !!0 [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/IntrinsicFunctions::UnboxGeneric<class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<int32,float64>>(object)
IL_000a: ldarg.0
IL_000b: tail.
IL_000d: callvirt instance !1 class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<int32,float64>::Invoke(!0)
IL_0012: ret
} // end of method Program::float_unit
See my answer to this question:
which suggests this today:
[<Measure>]
type s
let intWithSecondsMeasure = 1<s>
let intUtoFloatU< [<Measure>] 'u>( x : int<'u> ) : float<'u> = //'
let i = int x // drop the units
let f = float i // cast
box f :?> float<'u> //' restore the units
let floatWithS = intUtoFloatU intWithSecondsMeasure
来源:https://stackoverflow.com/questions/1894250/f-unit-of-measure-casting-without-losing-the-measure-type