Assuming your C environment does 2's complement integers, then this is much simpler than it seems.
typedef long s1516; // 32bit 2's complement signed integer
s1516 floattos1516(double f) {
return (s1516)(f * 65536. + 0.5);
}
The representation is a fixed point value, with 16 bits of fraction. That is the same as a rational number whose denominator is always 65536 (or 216). To form such a rational from a floating point value, you just multiply by the denominator. Then it is just a matter of an appropriate rounding, and a truncation to the integral type.
The standard picked the form they did because this just works if your system uses 2's complement integer arithmetic. Although it is true that the leftmost bit does represent the sign, it is not a sign bit in the sense that is used in a floating point representation.
If your calculations are truly float
rather than double
, you will find that you don't have as much precision in your calculation as is available in the fixed point value for numbers near full scale. If you calculate in double
, then you will always have more precision in your calculation than in the result.
Edit:
The apparently latest spec is available from the ICC as Specification ICC.1:2004-10 (Profile version 4.2.0.0). Section 5.1.3:
5.1.3 s15Fixed16Number
A fixed signed 4-byte/32-bit quantity which has
16 fractional bits as shown in table 3.
Table 3 — s15Fixed16Number
Number Encoding
-32768,0 80000000h
0 00000000h
1,0 00010000h
32767 + (65535/65536) 7FFFFFFFh
Aside from localized preference for the representation of a decimal point, these values are completely consistent with my understanding that the representation is simply signed 2's complement integers that should be divided by 65536 to get their values.
The natural conversion to the representation is simply to multiply by 65536, and from it simply to divide. Picking a suitable rounding rule is a matter of preference.
The full scale range is from -32768.0 (0x80000000) to approximately 32767.9999847412 (0x7fffffff), inclusive.
I would agree that it would be clearer if the specification had happened to show the representation in hex of any negative values. I skimmed the entire document, and the only values I found represented in both decimal and hex were CIE XYZ chromaticity coordinates, which by definition range from 0 to 1, and hence don't help as exemplar negative values.