Hello,
I was wondering if it's possible to convert a float64 into 8 uint8 s. To be more precise, I need to add the float64 into a seq[uint8], and then have a way of converting the corresponding bytes back to a float64.
Can this be achieved? If so, how?
import parseutils, os
func flt2arr (x: float64) : array[0..7, uint8] =
result = cast[array[0..7, uint8]](x)
func arr2flt (a: array[0..7, uint8]) : float64 =
result = cast[float64](a)
when isMainModule:
let params = commandLineParams()
var flt : float64
discard parseFloat(params[0], flt)
let ary = flt2arr(flt)
for u in ary:
echo(u)
let arr : array[0..7, uint8]
= [ary[0]+1, ary[1], ary[2], ary[3], ary[4], ary[5], ary[6], ary[7]]
echo(arr2flt(arr))
afaik atleast in C there's no "safe" way to do it. The method presented by @e works in most situations, but breaks strict aliasing (a poiner of one type may not be casted to a pointer of an incompatible type).
The usual safer way, in C is by using a union:
type
Uint8ToFloat64 {.union.} = object
f: float64
i: array[8, uint8]
proc floatToInts(f: float64): array[8, uint8] =
var convert: Uint8ToFloat64
convert.f = f
convert.i
proc intsToFloat(i: array[8, uint8]): float64 =
var convert: Uint8ToFloat64
convert.i = i
convert.f
doAssert intsToFloat(floatToInts(-42.0)) == -42.0
An object shall have its stored value accessed only by an lvalue expression that has one of the following types ... a character type.
The meaning of "a character type" here includes unsigned char, which is also uint8. So, making some assumptions about the Nim code generator, both approaches are well defined in C.
The following should also be safe, this translates to a memcpy() at the C level. This may seem like overkill, but the C compiler should recognize the use of memcpy for type punning and optimize it away and generate a register to register move.
proc flt2arr(f: var float64, a: var array[8, uint8]) =
assert sizeof(a) == sizeof(f)
copyMem(addr a[0], addr f, sizeof f)
proc arr2flt(a: var array[8, uint8], f: var float64) =
assert sizeof(a) == sizeof(f)
copyMem(addr f, addr a[0], sizeof f)
Sorry, I don’t see any aliasing in the code presented by e. The float value is copied into an array of uint8 and there is no sharing of memory. It would be different is one had used pointers, of course.
But, with unions, you have an aliasing as two fields refer to the same address in the union. Modifying one, automatically modifies the second (which is the intended purpose). So, unions are in fact theoretically less safe than cast on values which have only a local action.
Of course, this is seldom a problem as we are supposed to know what we are doing when using unions.
the reason I thought this way, was because I translated the Nim code in my head to C, to something like that:
void flt2arr(double x, uint8_t[] result) {
for(int i = 0; i < 8; i++)
result[i] = (uint8_t*)&x + i;
}
But as @araq said, this is not what the output of the Nim compiler is