I propose to just try it using float and if that fails (due to range overflow) use the double version:
try:
binary = struct.pack('>f', value)
except OverflowError:
binary = struct.pack('>d', value)
The range is the only aspect in which your question makes perfect sense.
If it comes to precision, your question loses to make sense because, as you say, Python always uses doubles internally, and even a simple 3.3
is, packed and unpacked as float, only 3.299999952316284
afterwards:
struct.unpack('>f', struct.pack('>f', 3.3))
(3.299999952316284,)
So virtually no double can be represented as a float. (Typically none that isn't an int or otherwise coming out of a float originally.)
You could, however, make a check whether the packed-unpacked version of your number equals the original, and if it does, use the float version:
try:
binary = struct.pack('>f', value)
if struct.unpack('>f', binary)[0] != value:
binary = struct.pack('>d', value)
except OverflowError:
binary = struct.pack('>d', value)