13
\$\begingroup\$

Background

IEEE 754 Double-precision floating-point format is a way to represent real numbers with 64 bits. It looks like the following:

A real number n is converted to a double in the following manner:

  1. The sign bit s is 0 if the number is positive, 1 otherwise.
  2. The absolute value of n is represented in the form 2**y * 1.xxx, i.e. a power-of-2 times a base.
  3. The exponent e is y (the power of 2) plus 1023.
  4. The fraction f is the xxx part (fractional part of the base), taking the most significant 52 bits.

Conversely, a bit pattern (defined by sign s, exponent e and fraction f, each an integer) represents the number:

(s ? -1 : 1) * 2 ** (e - 1023) * (1 + f / (2 ** 52))

Challenge

Given a real number n, output its 52-bit fraction part of the double representation of n as an integer.

Test Cases

0.0        =>                0
16.0       =>                0
0.0625     =>                0
1.2        =>  900719925474099 (hex 3333333333333)
3.1        => 2476979795053773 (hex 8cccccccccccd)
3.5        => 3377699720527872 (hex c000000000000)
10.0       => 1125899906842624 (hex 4000000000000)
1234567.0  =>  798825262350336 (hex 2d68700000000)
1e-256     => 2258570371166019 (hex 8062864ac6f43)
1e+256     => 1495187628212028 (hex 54fdd7f73bf3c)

-0.0       =>                0
-16.0      =>                0
-0.0625    =>                0
-1.2       =>  900719925474099 (hex 3333333333333)
-3.1       => 2476979795053773 (hex 8cccccccccccd)
-3.5       => 3377699720527872 (hex c000000000000)
-10.0      => 1125899906842624 (hex 4000000000000)
-1234567.0 =>  798825262350336 (hex 2d68700000000)
-1e-256    => 2258570371166019 (hex 8062864ac6f43)
-1e+256    => 1495187628212028 (hex 54fdd7f73bf3c)

You can check other numbers using this C reference which uses bit fields and a union.

Note that the expected answer is the same for +n and -n for any number n.

Input and Output

Standard rules apply.

Accepted input format:

  • A floating-point number, at least having double precision internally
  • A string representation of the number in decimal (you don't need to support scientific notation, since you can use 1000...00 or 0.0000...01 as input)

For output, a rounding error at the least significant bit is tolerable.

Winning Condition

This is , so the lowest bytes in each language wins.

\$\endgroup\$
7
  • \$\begingroup\$ Sandbox post (deleted) \$\endgroup\$
    – Bubbler
    Commented Mar 31, 2018 at 7:53
  • 1
    \$\begingroup\$ The test cases include only non-negative numbers. Can the input be negative? \$\endgroup\$
    – Dennis
    Commented Mar 31, 2018 at 15:06
  • \$\begingroup\$ @Dennis Yes. I'll add some more test cases. \$\endgroup\$
    – Bubbler
    Commented Mar 31, 2018 at 15:58
  • 3
    \$\begingroup\$ Your description of the IEEE floating point format doesn't mention denormal numbers which are interpreted in a slightly different way (no implicit leading 1). Do denormals have to be handled correctly? \$\endgroup\$
    – nwellnhof
    Commented Mar 31, 2018 at 16:01
  • 1
    \$\begingroup\$ @nwellnhof You don't need to consider denormals, NaN and Infinity. \$\endgroup\$
    – Bubbler
    Commented Mar 31, 2018 at 18:09

21 Answers 21

8
\$\begingroup\$

C (gcc), 42 30 bytes

long f(long*p){p=*p&~0UL>>12;}

Takes a pointer to a double as argument and returns a long.

Requires 64-bit longs and gcc (undefined behavior).

Thanks to @nwellnhof for -2 bytes!

Try it online!

\$\endgroup\$
2
  • \$\begingroup\$ &~0UL>>12 is two bytes shorter. The macro only works with lvalues, though. \$\endgroup\$
    – nwellnhof
    Commented Mar 31, 2018 at 15:43
  • \$\begingroup\$ Use macro -Df(x)=*(long *)&x&~0UL>>12, save 3 bytes. TIO \$\endgroup\$
    – GPS
    Commented Apr 1, 2018 at 18:26
6
\$\begingroup\$

Haskell, 27 31 bytes

(`mod`2^52).abs.fst.decodeFloat

decodeFloat returns the significand and the exponent, but for some reason the former is 53 bit in Haskell, so we have to cut one bit off.

Try it online!

\$\endgroup\$
0
5
\$\begingroup\$

MATL, 10 bytes

IZ%52W\0YA

Try it online!

Explanation

        % Implicit input
IZ%     % Cast to uint64 without changing underlying byte representation
52W     % Push 2^52
\       % Modulus
0YA     % Convert to decimal. Gives a string. This is needed to avoid
        % the number being displayed in scientific notation
        % Implicit display
\$\endgroup\$
5
\$\begingroup\$

Python 3, 54 50 bytes

f=lambda x:int(x.hex().split('.')[1].split('p')[0],16)

Try it online!

With Kirill's suggestion:

f=lambda x:int(x.hex()[4+(x<0):].split('p')[0],16)

Try it online!

\$\endgroup\$
4
  • \$\begingroup\$ I might be wrong, but I think Python's hex() gives normalized notation that always starts with 0x1.. If so, you could just use this for 44 bytes. \$\endgroup\$
    – Kirill L.
    Commented Mar 31, 2018 at 17:40
  • 1
    \$\begingroup\$ Well, I forgot about negative numbers, so it looks like 50 bytes after all. \$\endgroup\$
    – Kirill L.
    Commented Mar 31, 2018 at 18:15
  • \$\begingroup\$ @kirill-l It doesn't always start with "1." (see for example (2**-1028)) but the OP doesn't say anything about subnormals, so I guess your second suggestion is acceptable. Please feel free to edit. \$\endgroup\$
    – Luca Citi
    Commented Mar 31, 2018 at 18:41
  • \$\begingroup\$ Actually in a recent comment the OP explicitly says we can safely ignore subnormals. \$\endgroup\$
    – Luca Citi
    Commented Mar 31, 2018 at 18:43
5
\$\begingroup\$

x86_64 machine language for Linux, 14 bytes

0:       66 48 0f 7e c0          movq   %xmm0,%rax
5:       48 c1 e0 0c             shl    $0xc,%rax
9:       48 c1 e8 0c             shr    $0xc,%rax
d:       c3                      retq

Try it online!

EDIT: Thanks to @Bubbler

\$\endgroup\$
2
  • \$\begingroup\$ try and use your own CC instead of the standard ABI. By requiring the double be in rax, you can easily drop the entire move from xmm0. Only change needed for this is to make the test framework in ASM rather than C (Unless GCC is extra smart). \$\endgroup\$ Commented Apr 29, 2018 at 2:48
  • 1
    \$\begingroup\$ Fixed TIO \$\endgroup\$
    – Bubbler
    Commented Sep 22, 2022 at 0:19
4
\$\begingroup\$

JavaScript (ES7), 52 50 bytes

f=n=>n?n<0?f(-n):n<1?f(n*2):n<2?--n*2**52:f(n/2):0
<input oninput=o.textContent=f(this.value)><pre id=o>0

Not using Math.floor(Math.log2(n)) because it's not guaranteed to be accurate. Edit: Saved 2 bytes thanks to @DanielIndie.

\$\endgroup\$
2
  • \$\begingroup\$ why not --n*2**52 \$\endgroup\$ Commented Mar 31, 2018 at 12:57
  • \$\begingroup\$ @DanielIndie Because I forgot that that golf works with floats... \$\endgroup\$
    – Neil
    Commented Mar 31, 2018 at 16:21
3
\$\begingroup\$

Perl 5 -pl, 28 bytes

$_=-1>>12&unpack Q,pack d,$_

Try it online!

The 1e-256 and 1e256 test cases are off but that's because Perl 5 converts huge or tiny floating point strings inexactly.

\$\endgroup\$
2
\$\begingroup\$

C (gcc) macro, 49 bytes

-DF(x)=x?ldexp(frexp(fabs(x),(int[1]){})-.5,53):0

Try it online!

Returns a double but assuming IEEE precision, it won't have a fractional part. Also handles negative numbers now.

\$\endgroup\$
0
2
\$\begingroup\$

T-SQL, 80 bytes

SELECT CAST(CAST(n AS BINARY(8))AS BIGINT)&CAST(4503599627370495AS BIGINT)FROM t

The input is taken from the column n of a table named t:

CREATE TABLE t (n FLOAT)
INSERT INTO t VALUES (0.0),(1.2),(3.1),(3.5),(10.0),(1234567.0),(1e-256),(1e+256)

SQLFiddle

\$\endgroup\$
2
\$\begingroup\$

Hoon, 25 bytes

|*(* (mod +< (pow 2 52)))

Create a generic function that returns the input mod 2^52.

Calling it:

> %.  .~1e256
  |*(* (mod +< (pow 2 52)))
1.495.187.628.212.028
\$\endgroup\$
1
  • \$\begingroup\$ I never thought I would see hoon here. I tried to understand urbit a couple of years back, but couldn't really make heads or tails of it. \$\endgroup\$
    – recursive
    Commented Mar 31, 2018 at 20:04
2
\$\begingroup\$

JavaScript (ES7), 98 76 bytes

Saved 22 (!) bytes thanks to @Neil

More verbose than Neil's answer, but I wanted to give it a try with typed arrays.

(n,[l,h]=new Uint32Array(new Float64Array([n]).buffer))=>(h&-1>>>12)*2**32+l

Try it online!

\$\endgroup\$
2
  • \$\begingroup\$ ES7 + UInt32Array saves 22 bytes: (n,[l,h]=new Uint32Array(new Float64Array([n]).buffer))=>(h&-1>>>12)*2**32+l \$\endgroup\$
    – Neil
    Commented Mar 31, 2018 at 19:52
  • \$\begingroup\$ Is there any interpreter which had implemented BigInt64Array already? \$\endgroup\$
    – tsh
    Commented Apr 3, 2018 at 2:14
2
\$\begingroup\$

APL (Dyalog), 38 bytes

{0=⍵:0⋄(2*52)ׯ1+×∘2⍣(1≤⊣)÷∘2⍣(1>⊣)|⍵}

Try it online!

\$\endgroup\$
2
\$\begingroup\$

Stax, 19 14 bytes

üâïc-Hò~÷]ó┬ó♪

Run and debug it

Unpacked, ungolfed, and commented, the code looks like this.

|a      absolute value
{HcDw   double until there's no fractional part
@       convert to integer type
:B      convert to binary digits
D52(    drop the first digit, then pad to 52
:b      convert back number

Run this one

\$\endgroup\$
1
\$\begingroup\$

Rust, 21 bytes

|p|p.to_bits()&!0>>12

Pretty much copied C solution. Takes an f64 argument.

Try it online!

\$\endgroup\$
1
\$\begingroup\$

Forth (gforth), 42 bytes

Assumes floats are double by default and cells are 8 bytes in length (as is the case on my computer and TIO)

: f f, here float - @ $fffffffffffff and ;

Try it online!

Explanation

f,             \ take the top of the floating point stack and store it in memory
here float -   \ subtract the size of a float from the top of the dictionary
@              \ grab the value at the address calculated above and stick it on the stack
$fffffffffffff \ place the bitmask (equivalent to 52 1's in binary) on the stack
and            \ apply the bitmask to discard the first 12 bits

Forth (gforth) 4-byte cell answer, 40 bytes

Some older forth installations default to 4-byte cells, instead

: f f, here float - 2@ swap $FFFFF and ;

Explanation

f,             \ take the top of the floating point stack and store it in memory
here float -   \ subtract the size of a float from the top of the dictionary
2@             \ grab the value at the address above and put it in the top two stack cells
swap           \ swap the top two cells put the number in double-cell order
$fffff         \ place the bitmask (equivalent to 20 1's in binary) on the stack
and            \ apply the bitmask to discard the first 12 bits of the higher-order cell
\$\endgroup\$
1
\$\begingroup\$

Go, 68 bytes

import."math"
func d(n float64)uint64{return Float64bits(n)<<12>>12}

Can't do the &-1 trick because Go disallows overflows.

Attempt This Online!

\$\endgroup\$
0
\$\begingroup\$

Ruby, 39 bytes

->n{[n].pack(?D).unpack(?Q)[0]&~-2**52}

Try it online!

\$\endgroup\$
0
\$\begingroup\$

Java 8 or later, 38 bytes

x->Double.doubleToLongBits(x)&-1L>>>12

Try it online!

\$\endgroup\$
0
\$\begingroup\$

Aarch64 machine language for Linux, 12 bytes

0:   9e660000        fmov x0, d0
4:   9240cc00        and  x0, x0, #0xfffffffffffff
8:   d65f03c0        ret

To try this out, compile and run the following C program on any Aarch64 Linux machine or (Aarch64) Android device running Termux

#include<stdio.h>
const char f[]="\0\0f\x9e\0\xcc@\x92\xc0\3_\xd6";
int main(){
  double io[] = { 0.0,
                  1.2,
                  3.1,
                  3.5,
                 10.0,
            1234567.0,
               1e-256,
               1e+256,
                 -0.0,
                 -1.2,
                 -3.1,
                 -3.5,
                -10.0,
           -1234567.0,
              -1e-256,
              -1e+256 };

  for (int i = 0; i < sizeof io / sizeof*io; i++) {
    double input = io[i];
    long output = ((long(*)(double))f)(io[i]);

    printf("%-8.7g => %16lu (hex %1$013lx)\n", input, output);
  }
}
\$\endgroup\$
0
\$\begingroup\$

Julia 0.4, 30 bytes

x->reinterpret(UInt,x)<<12>>12

Try it online!

\$\endgroup\$
0
\$\begingroup\$

05AB1E, 18 bytes

Ä[ÐïQ#·}b0₃׫À52£C

Port of @recursive's Stax answer.

Try it online or verify all test cases.

Alternatively:

_+Ä[DbD53∍©›#·}®¦C

Try it online or verify all test cases.

Alternatively (by porting @LucaCiti's Python answer in the legacy version of 05AB1E):

Ä".hex()"«.E„.p¡Hà

Try it online or verify all test cases.

Explanation:

Ä             # Get the absolute value of the (implicit) input-float
 [            # Start an infinite loop:
  Ð           #  Triplicate the current value
   ï          #  Cast the top to an integer
    Q         #  Check if the top two values are the same†
     #        #  If they are: stop the infinite loop
  ·           #  Double the current float
 }b           # After the loop: convert the integer to a binary string
   0₃׫       # Append 95 trailing 0s
              # (95 is the smallest single-byte constant >= 52)
       À      # Rotate it once towards the left, so the leading 1 becomes trailing
        52£   # Only keep the first 52 bits
           C  # Convert it from a binary string to a base-10 integer
              # (which is output implicitly as result)
_             # Check if the (implicit) input-float is 0
 +            # Add it to the (implicit) input-float so it becomes 1
  Ä           # Convert it to its absolute value
[             # Start an infinite loop:
 D            #  Duplicate the current value
  b           #  Convert its integer part to a binary string
   D          #  Duplicate this string
    53∍       #  Extend/shorten it to size 53
       ©      #  Store this result in variable `®`
        ›     #  Check if it's larger than the binary string
         #    #  If it is: stop the infinite loop
 ·            #  Double the current float
}®            # After the loop: push binary string `®` which we've saved
  ¦           # Remove its leading 1
   C          # Convert it from a binary string to a base-10 integer
              # (which is output implicitly as result)
Ä             # Get the absolute value of the (implicit) input-float††
 ".hex()"«    # Append string ".hex()"
          .E  # Evaluate and execute it as Python code
„.p¡          # Split it on both the "." and "p"
    H         # Convert each inner part from a hexadecimal string to an integer
     à        # Push the maximum of this list
              # (which is output implicitly as result)

† If 05AB1E didn't had this bug and the input format is also guaranteed to not contain any additional trailing 0 besides #.0 (e.g. 12.0 is a valid input, but 12 or 12.10 aren't), the DïQ could have been ¤_ - which checks if the float contains a trailing zero.

†† Unlike the Python answer, .hex() doesn't work on negative values in the legacy version of 05AB1E, unless I add parenthesis around it (e.g. -1.0.hex() doesn't work, but 1.0.hex() and (-1.0).hex() both do: try it online), which is why the Ä was necessary in the third program.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.