The task is simple: given a 32 bit integer, convert it to its floating point value as defined by the IEEE 754 (32-bit) standard.
To put it another way, interpret the integer as the bit-pattern of an IEEE binary32 single-precision float and output the numeric value it represents.
IEEE 754 single precision
Here is a converter for your reference.
Here is how the format looks, from Wikipedia's excellent article:
The standard is similar to scientific notation.
The sign bit determines whether the output is negative or positive. If the bit is set, the number is negative otherwise it is positive.
The exponent bit determines the exponent (base 2), it's value is offset by 127. Therefore the exponent is \2ドル^{n-127}\$ where n is the integer representation of the exponent bits.
The mantissa defines a floating point number in the range \$[1,2)\$. The way it represents the number is like binary, the most significant bit is \$\frac 1 2\$, the one to the right is \$\frac 1 4\$, the next one is \$\frac 1 8\$ and so on... A one by default is added to the value, implied by a non-zero exponent.
Now the final number is: $$\text{sign}\cdot 2^{\text{exponent}-127}\cdot \text{mantissa}$$
Test cases
1078523331 -> 3.1400001049041748046875
1076719780 -> 2.71000003814697265625
1036831949 -> 0.100000001490116119384765625
3264511895 -> -74.24919891357421875
1056964608 -> 0.5
3205496832 -> -0.5625
0 -> 0.0
2147483648 -> -0.0 (or 0.0)
For this challenge assume that cases like NaN
and inf
are not going to be the inputs, and subnormals need not be handled (except for 0.0
which works like a subnormal, with the all-zero exponent implying a leading 0 bit for the all-zero mantissa.) You may output 0
for the case where the number represented is -0
.
This is code-golf, so the shortest answer in bytes wins.
21 Answers 21
Python, 55 bytes
Correct as per original challenge description (always add 2^23 to mantissa) but not per IEEE.
lambda i:-(i>>31or-1)*2**((i>>23)%256-129)*(i/8**7%4+4)
Direct bit twiddling, no casting.
Python, 69 bytes
At last proper IEEE, I think (thanks @Neil).
lambda i:-(i>>31or-1)*2**((e:=i>>23&255)-126-(e>0))*(i/2**23%1+(e>0))
Python NumPy, 47 bytes
lambda i:int32(i).view("f4")
from numpy import*
Boring use of builtin "view" or "reinterpret" casting. Note that we can save the "u" from uint32
without issues.
-
2\$\begingroup\$ The no-casting version seems to fail for the testcases
0
and2147483648
. \$\endgroup\$alephalpha– alephalpha2022年10月11日 11:16:51 +00:00Commented Oct 11, 2022 at 11:16 -
2\$\begingroup\$ The "Proper IEEE" version actually halves subnormals. \$\endgroup\$Neil– Neil2022年10月12日 19:03:16 +00:00Commented Oct 12, 2022 at 19:03
-
\$\begingroup\$ Yeah, this look right, now.
2^(-126) * 0.mantissa
instead of2^(-127) * 1.mantissa
. An all-zero exponent encodes the same power of 2 as the minimum normalized float, instead of (not as well) changing the mantissa interpretation, so there isn't a gap in which values can be represented. My edit to fix the question's 0 handling didn't mention that detail. :/ (Fun fact: even 80-bit x87 IEEE extended precision with an explicit leading-1 bit works this way, so the leading 1 is always redundant.) \$\endgroup\$Peter Cordes– Peter Cordes2022年10月15日 11:03:17 +00:00Commented Oct 15, 2022 at 11:03
x86 32-bit machine code, 5 bytes
D9 44 24 04 C3
Following the cdecl
calling convention, this takes the 32-bit integer on the stack and returns the result on the FPU register stack.
In assembly:
f: fld DWORD PTR [esp + 4]
ret
(fld
does everything that is needed. The integer is placed below the return address on the stack, hence the + 4
to get to it.)
-
3\$\begingroup\$ x64 works similarly well with "movd xmm0,edi". \$\endgroup\$throx– throx2022年10月11日 06:36:36 +00:00Commented Oct 11, 2022 at 6:36
-
\$\begingroup\$ Note that what
fld m32
does is convert from binary32 to the x87 80-bit format inst0
. So it widens the exponent field and appends zeros to the mantissa, and also creates an explicit leading bit of the mantissa according to the exponent being non-zero. This is how legacy x86 code normally returns FP values; the caller of this function can recover the originalfloat
with anfstp m32
instruction, or get it as adouble
withfstp m64
, or get the 80-bit value withfstp m80
. \$\endgroup\$Peter Cordes– Peter Cordes2022年10月11日 22:50:48 +00:00Commented Oct 11, 2022 at 22:50 -
\$\begingroup\$ This is fine: the question asks for any FP or numeric value that holds the value represented by the binary32. It doesn't require a type-pun to an actual
float
binary32 return value. Which makes sense; many languages only have double-precision floating point, if they have non-integers at all. \$\endgroup\$Peter Cordes– Peter Cordes2022年10月11日 22:52:59 +00:00Commented Oct 11, 2022 at 22:52 -
\$\begingroup\$ @throx: This is a challenge where a custom calling convention completely trivializes it. e.g. take the value by reference and update it in place, just a 1-byte
ret
. Or take the integer arg in XMM0; that's probably more justifiable than returning an FP bit-pattern in an integer reg. Although ARM soft-float calling conventions do pass FP bit-patterns in integer regs, and the return value regr0
is also the first arg-passing reg. godbolt.org/z/x7T6W8xzT \$\endgroup\$Peter Cordes– Peter Cordes2022年10月12日 03:48:50 +00:00Commented Oct 12, 2022 at 3:48 -
1\$\begingroup\$ @throx: Sorry, my comment was badly phrased. You're correct that x86-64 SysV or Windows x64 would both need 5-byte functions. But we can get shorter if we consider alternatives as recommended by Tips for golfing in x86/x64 machine code, like a 1-byte
C3 ret
. A bit hard to justify for x86, although I posted an answer with some discussion of it. But justifiable for ARM with a soft-float ABI, for a 2-byte answer, which is the first part of the answer I wrote. \$\endgroup\$Peter Cordes– Peter Cordes2022年10月12日 04:26:30 +00:00Commented Oct 12, 2022 at 4:26
Rust, 14 bytes
f32::from_bits
Not even reached the 30 byte min limit for posts
-
1\$\begingroup\$ Don't codegolf answers have to be functions, lambdas, or programs? Not just code fragments or the name of a built-in function. I think it has to be something that would let later code do
f(1234)
, i.e. something that lets later call-sites use a custom (short) name for this operation. \$\endgroup\$Peter Cordes– Peter Cordes2022年10月11日 22:34:53 +00:00Commented Oct 11, 2022 at 22:34 -
1\$\begingroup\$ Since you can do
let f = f32::from_bits; f(5)
in the same way you can dolet f = |x|f32::from_bits(x)
I think it's valid. There are a lot of other answers that use this technique too, like this one \$\endgroup\$mousetail– mousetail2022年10月12日 06:09:21 +00:00Commented Oct 12, 2022 at 6:09 -
\$\begingroup\$ Ok, that makes sense. If we're going to allow lambdas that require the surrounding code to give it a name if they want to reuse it, and bare function names can be used the same way, it wouldn't make sense to disallow them. \$\endgroup\$Peter Cordes– Peter Cordes2022年10月12日 06:56:48 +00:00Commented Oct 12, 2022 at 6:56
C (GCC) without reliance on undefined behaviour, (削除) 41 (削除ここまで) 40 bytes
#define f(x)((union{int a;float b;})x).b
Type-punning through pointers does normally work, but the compiler is free to do strange optimisations which can stop it working. A union makes this explicit in ways the compiler understands. It can also potentially be evaluated at compile time instead of at run time.
Note that it's best practise in C/C++ to use parentheses around the input value to a function-like macro and around the result, so you don't get unwanted interactions with precedence rules if this is used in a more complex statement. This would add 4 extra bytes to the total. For the tests defined in the question, we don't need this.
(Thanks @ceilingcat for spotting an unneeded space.)
-
\$\begingroup\$ Welcome to Code Golf! \$\endgroup\$2022年10月11日 14:42:52 +00:00Commented Oct 11, 2022 at 14:42
-
3\$\begingroup\$ Fun fact: this is well-defined in ISO C99 (assuming
int
andfloat
are the same width), but not in ISO C++. It is well-defined in GNU C++, and in Visual C++, going beyond what ISO C++ defines. (In ISO C++, the safe ways to type-pun arestd::memcpy
and C++20std::bit_cast<float>(x)
). The reason for type-punning via pointers working is that MSVC explicitly supports it, and modern GCC tries to notice idioms like that and sometimes not break them even with the default-fstrict-aliasing
. Older GCC versions would happily break*(float*)&x
even though it sees the&
, cast, and deref. \$\endgroup\$Peter Cordes– Peter Cordes2022年10月12日 02:57:30 +00:00Commented Oct 12, 2022 at 2:57 -
\$\begingroup\$ @PeterCordes: Does this answer with the union work with ISO C++? \$\endgroup\$pts– pts2022年10月12日 16:11:23 +00:00Commented Oct 12, 2022 at 16:11
-
1\$\begingroup\$ @pts: No. It was edited to add "/ C++" after I commented, despite my earlier comment pointing out it's not well-defined in ISO C++, only "C++ (GCC)" and some other specific implementations. Type punning between integer and array using `union`? / Unions and type-punning. It does only claim to be a "C / C++ (GCC)" answer, but I agree it would be much better to point out that it depends on a GNU extension for the C++ part, if it's going to talk about doing it without UB. \$\endgroup\$Peter Cordes– Peter Cordes2022年10月12日 18:47:48 +00:00Commented Oct 12, 2022 at 18:47
-
1\$\begingroup\$ @PeterCordes OK, I'll undo that edit - thanks for the feedback. \$\endgroup\$Graham– Graham2022年10月14日 13:18:35 +00:00Commented Oct 14, 2022 at 13:18
-
1\$\begingroup\$ Welcome to Code Golf, and nice answer! \$\endgroup\$2022年10月11日 19:04:35 +00:00Commented Oct 11, 2022 at 19:04
-
3\$\begingroup\$ This does work in recent GCC and clang even without specifying
-std=gnu++20
orc++20
, so no need to worry about extra bytes for the command-line options you used in your Godbolt link. Also, yes, apparently MSVC used the same name as GCC/clang, following GCC's naming pattern for compiler built-ins in this case. Surprising. \$\endgroup\$Peter Cordes– Peter Cordes2022年10月12日 03:17:34 +00:00Commented Oct 12, 2022 at 3:17 -
\$\begingroup\$ Heh, I noticed it worked even without specifying that flag, but forgot to remove it from the TIO (and to count it as bytes...) \$\endgroup\$Fulgen– Fulgen2022年10月12日 18:54:30 +00:00Commented Oct 12, 2022 at 18:54
-
\$\begingroup\$ I'm not sure if
-std=c++20
needs to count as part of the answer, since C++20 is a standard language, and it just happens that current GCC needs an option to fully operate in C++20 mode because that's not yet the default. As opposed to Perl where some interpreter options add code to your program. A meta answer I from from a former mod mentioned that-m32
should count as zero bytes, since you could just as well have used GCC on a 32-bit system where that's the default. So I'm debunking the concern I raised about options last comment. \$\endgroup\$Peter Cordes– Peter Cordes2022年10月13日 10:29:30 +00:00Commented Oct 13, 2022 at 10:29 -
\$\begingroup\$ For the record, the only answer on Command-line flags on front ends proposes that flags should count as different languages, and has about 5:1 up:down votes. That's fully appropriate for C++20 vs. the default. But wonky for Perl, sed, and awk especially. \$\endgroup\$Peter Cordes– Peter Cordes2022年10月13日 10:38:41 +00:00Commented Oct 13, 2022 at 10:38
JavaScript (ES6), 50 bytes
-16 bytes (!) thanks to @Neil
n=>new Float32Array(new Int32Array([n]).buffer)[0]
-
1\$\begingroup\$
new Int32Array([n]).buffer
actually works on both little and big-endian architectures. \$\endgroup\$Neil– Neil2022年10月10日 19:56:12 +00:00Commented Oct 10, 2022 at 19:56 -
1\$\begingroup\$ From @loopywalt's Python answer, save another byte:
n=>(4+n/8**7%4)*2**((n<<1>>>24)-129)*(n>>31|1)
\$\endgroup\$Neil– Neil2022年10月10日 23:36:08 +00:00Commented Oct 10, 2022 at 23:36 -
2\$\begingroup\$ The no-casting versions seems to fail for the testcases
0
and2147483648
. \$\endgroup\$alephalpha– alephalpha2022年10月11日 11:18:02 +00:00Commented Oct 11, 2022 at 11:18 -
1\$\begingroup\$ @Neil: As long as float endianness matches integer endianness; apparently there have been some unfortunate historical architectures where that wasn't the case: Floating point Endianness? quotes wikipedia. But a newer quote from the same article says that all modern machines (using IEEE754) have matching int and FP endianness. \$\endgroup\$Peter Cordes– Peter Cordes2022年10月11日 23:00:07 +00:00Commented Oct 11, 2022 at 23:00
-
1\$\begingroup\$ @alephalpha:
0
(representing0.0
) is technically a subnormal value (or works like one): the exponent is all zero, so the implicit leading bit of the mantissa is 0, not 1. Some FP bithacks that fail for Inf/NaN/subnormals also fail for 0.0; I've seen that before in Why don't GCC and Clang optimize multiplication by 2^n with a float to integer PADDD of the exponent, even with -ffast-math? \$\endgroup\$Peter Cordes– Peter Cordes2022年10月11日 23:05:21 +00:00Commented Oct 11, 2022 at 23:05
Charcoal, 45 bytes
×ばつ+%θηηX2−ζ150∨‹θX2¦31±1
Try it online! Link is to verbose version of code. Explanation:
Nθ
Input the integer.
≔X2¦23η
Calculate 223
as it gets used often enough to make it worthwhile. (It was originally only used twice but it was still worthwhile then. I then golfed a byte off by introducing a third use, which also avoided the use of Incremented
which is buggy on TIO, otherwise I would have had to have used ATO instead.)
≔%÷θη256ζ
Extract the exponent. This is needed because an exponent of 0
needs to be special-cased. (Normally this results in a subnormal, but fortunately the only subnormals that we need to support are 0
and -0
.)
×ばつ
If the exponent is zero, output zero, otherwise output the product of...
+%θηη
... the bottom 23
bits of the input integer, with a 1
bit prepended, ...
X2−ζ150
... 2 to the power of the exponent, adjusted by 150
instead of 127
to shift the mantissa bits by 23
, and...
∨‹θX2¦31±1
... the sign bit.
ARM Thumb machine code, 2 bytes
arm-none-eabi-g++
defaults to -mfloat-abi=soft
, so float
is passed/returned in general-purpose integer registers. (Godbolt)
ARM's standard calling convention passes the first arg in r0
, which is also the return-value register. So all we need to do is return with bx lr
(2 bytes).
// float f(int)
// machine code hex // assembly
70 47 bx lr
The same trick can work for any ISA if you can justify a custom calling convention. Normally that's fine, but on machines that use IEEE754 FP the challenge reduces to type-punning, and a calling convention that trivializes it is less interesting. e.g. for x86, you could normally justify taking an integer arg in XMM0, which is where you'd want a scalar float
. (Tips for golfing in x86/x64 machine code)
x86-64 machine code, with custom calling convention, 1 byte
1-byte c3 ret
for x86-64.
Another justification could be that we take the input by reference and update in-place. Like C void f(void*p){}
- mutate the pointed-to int
object from int
to float
, which is a no-op in asm.
(As a C function, that wouldn't make it well-defined to point a float*
at an int
, still a strict-aliasing violation. It might make it work in practice if it couldn't be inlined, forcing the compiler to keep its hands off. But this is a machine code answer. Obviously in real asm you'd never call this, it doesn't do anything.)
x86-64 machine code with AMD64 System V calling convention, 5 bytes
It's the same length as a call
instruction, making it pointless not to inline, but whatever. :/
66 0f 6e c7 movd xmm0,edi
c3 ret
AVX vmovd xmm0,edi
is the same 4-byte length.
x86 with custom 3DNow! calling convention, 4 bytes
Did anyone ever use the low element of an mm register for scalar float with 3DNow!, like how SSE/SSE2 use the bottom of an xmm
register for scalar float/double? Possible, although there aren't scalar 3DNow! instructions like SSE addss
, only packed float like pfadd
. So you might get slowdown from subnormals in the high half. Still it's plausible.
# int arg in EDI, float return value in MM0
0f 6e c7 movd mm0,edi
c3 ret
C (gcc), (削除) 36 (削除ここまで) (削除) 34 (削除ここまで) (削除) 30 (削除ここまで) 23 bytes
#define f(x)*(float*)&x
-2 thanks to m90
-4 thanks to Digital Trauma and mousetail
-7 thanks to jdt
Unsafe code go brr
#define f(x)*(float*)&x // Macrotaking an int, returning a float
#define f(x) // Boilerplate
&x // Pointer to the input
(float*) // Reinterpret it as a pointer to a float instead of an int
* // Get the value at that pointer, now a float
-
6\$\begingroup\$ If you're willing to accept compilation with warnings (generally ok on CG), then x as an implicit int saves 4:
float f(x){return*(float*)&x;}
\$\endgroup\$Digital Trauma– Digital Trauma2022年10月10日 16:49:48 +00:00Commented Oct 10, 2022 at 16:49 -
4\$\begingroup\$ I think this is an UB (strict pointer aliasing) so compiler is free to return any value (unlessyou pass
-fno-strict-pointer-aliasing
or something else which will turn on compiler extention. \$\endgroup\$Maja Piechotka– Maja Piechotka2022年10月11日 09:49:25 +00:00Commented Oct 11, 2022 at 9:49 -
\$\begingroup\$ You should be able to omit
int
from the function declaration since the default type isint
\$\endgroup\$mousetail– mousetail2022年10月11日 10:58:48 +00:00Commented Oct 11, 2022 at 10:58 -
1\$\begingroup\$ This is definitely UB in C. \$\endgroup\$Marco– Marco2022年10月11日 14:58:50 +00:00Commented Oct 11, 2022 at 14:58
-
1\$\begingroup\$ On Linux Debian arm32, this also works:
f(x,y){*(int*)y=x;}
But then it is questionable if this does really meet the goal. You have to call it like this:float y; f(3264511895,&y);printf("%36.32f\n",y);
\$\endgroup\$12431234123412341234123– 124312341234123412341232022年10月11日 16:21:23 +00:00Commented Oct 11, 2022 at 16:21
Python, (削除) 69 (削除ここまで) (削除) 67 (削除ここまで) 65 bytes
lambda x:((x&8388607)/2**23+1)*2**((x>>23&255)-127)*(1-(x>>30&2))
-
2\$\begingroup\$ This seems to fail for the testcases
0
and2147483648
. \$\endgroup\$alephalpha– alephalpha2022年10月11日 11:18:39 +00:00Commented Oct 11, 2022 at 11:18 -
\$\begingroup\$ @alephalpha: The question didn't previously specify how
0
encoded+0.0
(like a subnormal where the all-zero exponent implies a leading zero for the mantissa); I edited the question. If zero and other subnormals weren't intended to be part of the challenge, that should be stated in the question and those test-cases removed. IMO answers that assume a non-zero exponent (i.e. answer the question as originally written) are interesting and worth keeping, even if people also want to add another longer version that does handle +-0.0. \$\endgroup\$Peter Cordes– Peter Cordes2022年10月12日 03:41:27 +00:00Commented Oct 12, 2022 at 3:41
MATL, 7 bytes
7Y%10Z%
Try it at MATL online! Or verify all test cases.
Code explanation
% Implicit input: number in 'double' data type
7Y% % Cast to 'uint32'
10Z% % Convert to 'single' without changing underlying data
% Implicit display
C++ (GCC), 30 bytes
[](int x){return*(float*)&x;};
This is just Seggan's C answer using C++11's lambda syntax instead of a named function.
-
\$\begingroup\$ Nitpick: It's C++11's lambda syntax, not C++10's. \$\endgroup\$Fulgen– Fulgen2022年10月12日 18:51:25 +00:00Commented Oct 12, 2022 at 18:51
-
\$\begingroup\$ If I understood the previous comments on that topic correctly, it's undefined behavior in C++. \$\endgroup\$Thomas Weller– Thomas Weller2022年10月13日 05:09:16 +00:00Commented Oct 13, 2022 at 5:09
J-uby, 27 bytes
Port of my Ruby answer.
-[I]|~:pack&?I|~:unpack1&?f
Explanation
-[I] | # Construct an array with one element (the input), then
~:pack & ?I | # pack it into a binary string
~:unpack1 & ?f | # unpack it into a float
-
\$\begingroup\$ To be understood in a particular way when the input exceeds
2**31
, like the stated case3264511895
(since C# can care about signedint
versus unsigneduint
). \$\endgroup\$Jeppe Stig Nielsen– Jeppe Stig Nielsen2022年10月13日 07:34:50 +00:00Commented Oct 13, 2022 at 7:34
Explore related questions
See similar questions with these tags.
3.14000010490417
in the first case \$\endgroup\$+-0.0
. I edited, assuming that those test cases implied this was part of the challenge. If not, the OP should remove the +-0.0 test cases. The all-zero exponent encoding implies a leading 0 bit for the all-zero mantissa, so it's2^(-126) * 0.mantissa
instead of2^(-127) * 1.mantissa
. en.wikipedia.org/wiki/… . Since you don't need to handle other cases of all-zero exponent, you could special-case the whole bit-pattern, e.g.if(! x<<1) return 0;
\$\endgroup\$