Recently I have been wrestling with Excel due to the 15 significant digits display limit for a number. I have been looking for a way to display the IEEE 754 format of a value contained in a Excel cell (since they are documented to work that way).
I'd prefer not to rely on VBA for this project (although I might get tempted if a memcpy
-like solution is possible).
See my answer below for my current implementation. Any input or alternative is appreciated. I choose to believe that I missed an easier, well-tested solution.
The following sequence allow me to convert a number to its IEEE 754 hexadecimal representation using Excel formulas. I did not try to handle any exceptions besides 0. From Cell A1 to G1:
A1: 0.123456
B1: =INT(LOG(ABS(A1),2))
Exponent
C1: =ABS(A1)/(2^B1)
Mantissa
D1: =(C1-1)*(2^52)
Convert mantissa to decimal
E1: =DEC2HEX(1023+B1+IF(A1<0,2^11,0),3)
Convert sign & exponent to hex
F1: =CONCATENATE(DEC2HEX(D1/2^32,5),DEC2HEX(MOD(D1,2^32),8))
Convert decimal to hex.
G1: ="0x"&IF(A1=0,0,E1&F1)
A few of my result:
=1.35632902954101*2^14
> 0x40D5B3861187E7A7EDIT: Follow-up to chux comments.
We can see that the following value give a wrong result due to a rounding error:
=255+0.9999999999999
> 0x40700000FFFFFFFEUnder this scenario, the value given at the step D1 is negative. If I use this information to update my exponent, my results appear to be consistent:
=255+0.9999999999999
> 0x406FFFFFFFFFFFFCHere is the updated B1 formula:
=IF((ABS(A1)/(2^INT(LOG(ABS(A1),2)))-1)*(2^52)<0,INT(LOG(ABS(A1),2))-1,INT(LOG(ABS(A1),2)))
EDIT2: above steps in a single function using LET (Available in Microsoft 365)
=LET(num,A1,
exp,INT(LOG(ABS(num),2)),
exponent,IF((ABS(num)/(2^exp)-1)*(2^52)<0,exp-1,exp),
mantissa,(ABS(num)/(2^exponent)-1)*(2^52),
part_a,DEC2HEX(1023+exponent+IF(num<0,2^11,0),3),
part_b,CONCATENATE(DEC2HEX(mantissa/2^32,5),DEC2HEX(MOD(mantissa,2^32),8)),
"0x"&IF(num=0,0,part_a&part_b))
EDIT3: as the point was already raised twice, I am adding a "most likely correct" answer for 32-bit float format.
=LET(num,A1,
exp,INT(LOG(ABS(num),2)),
exponent,IF((ABS(num)/(2^exp)-1)*(2^23)<0,exp-1,exp),
base_mantissa,(ABS(num)/(2^exponent)-1)*(2^23),
round_nearest_tie_even,IF(base_mantissa-INT(base_mantissa)=0.5,EVEN(base_mantissa),ROUND(base_mantissa,0)),
"0x"&IF(num=0,0,DEC2HEX((127+exponent+IF(num<0,2^8,0))*(2^23)+round_nearest_tie_even,8)))
This was less straightforward than expected: