Display the exponent from a binary floating point number as a decimal value

Had my software final exams recently, one of the last questions had me thinking for a while after the exam had finished.

Background

IEEE754 numbers are according to the below layout

The exponent is stored as n + 127 so for an exponent of 20 the exponent in binary would be: 127 + 20 = 147 then 10010011

The Challange

Write a program that when given text representing a binary floating point number to the IEEE754 standard, returns the exponent given as a decimal value.

Example

Input: 00111101110011001100110011001101 (0.1)

Isolated exponent bits: 01111011

Convert to binary: 0 + 64 + 32 + 16 + 8 + 0 + 2 + 1 = 123

Remove offset: 123 - 127 = -4

Output -4

Input: 10111111110111101011100001010010 (-1.64)

Isolated exponent bits: 01111111

Convert to binary: 0 + 64 + 32 + 16 + 8 + 4 + 2 + 1 = 127

Remove offset: 127 - 127 = 0

Output 0

Rules

• Input must be text/string/equivalent indexable datatype, eg. strings not integer or numerical
• Output can be any type, as long as it is decimal
• Aim for shortest solutions

Adapted from the 2020 Software Design and Development HSC Paper: 30) d) ii

Design an algorithm that takes in a string of 32 characters representing a floating point number, and displays the exponent as its signed decimal value.

Any feedback is appreciated as this is my first question

• Can we take the input as a list of 1s and 0s?
Nov 5 '20 at 9:50
• All challenges here must have an objective scoring criterion, for which we usually recommend code-golf unless you have something else in mind. Nov 5 '20 at 9:50
• Also, if you have another challenge idea, please use the sandbox to gather feedback from other users and fix potential problems. It is recommended to leave a challenge there for at least 3 days before posting it to main. Nov 5 '20 at 10:03
• Btw, welcome to the site! Nice first challenge. In the future, consider using the Sandbox. Had it been there, I might have suggested taking a variable length input representing a binary float with 16/32/64/128/265 bits.
Nov 5 '20 at 10:05
• @Adám I would of submitted it there but was unable to as i have less than 5 reputation Nov 5 '20 at 10:25

foldl1(\x y->x*2+y-1).take 8.tail

Try it online!

Input is a list of integers 0 or 1.

It's slightly shorter to foldl1 by x*2+y-1 than to subtract 127 at the end. This works because

foldl1 (\x y->x*2+y-1) [a,b,c,d,e,f,g,h]
= ((((((a*2+b-1)*2+c-1)*2+d-1)*2+e-1)*2+f-1)*2+g-1)*2+h-1
= a*128 + b*64-64 + c*32-32 + d*16-16 + e*8-8 + f*4-4 + g*2-2 + h-1
= 128a + 64b + 32c + 16d + 8e + 4f + 2g + h - 127
= fromBinary[a,b,c,d,e,f,g,h]-127

Python 3, 26 bytes

lambda s:int(s[1:9],2)-127

Try it online!

• How dare you beat me by 5 seconds with the exact same code! Nov 5 '20 at 10:04
• @pxeger I wrote a full test suite so as not to rush you! :D Nov 5 '20 at 10:05

C (gcc), 55 $$\\cdots$/extract_tex] 40 36 bytes Saved 2 6 bytes thanks to ceilingcat!!! Saved 2 a whopping 13 bytes thanks to Arnauld!!! #define f(s)strtol(s+1,s[9]=0,2)-127 Try it online! • Nov 5 '20 at 12:13 • @Arnauld Had to tweek it a bit to get it working for other testcases and saved another byte! Great idea - thanks! :D Nov 5 '20 at 13:58 x86 machine code, 14 bytes Same machine code works in 32 or 64-bit mode. (Same length for 16-bit mode would be easy with different regs). Callable from C as x86-64 System V int8_t getexp(const char *b2_str); 23 ; input: char *str in RDI 24 ; return: int8_t AL; (calling convention allows garbage in high bytes of RAX) 25 getexp_scalar: 26 00000030 6A09 push 9 ; sign bit gets shifted out the top. 27 00000032 59 pop rcx 28 00000033 B030 mov al, '0' 29 .loop: 30 00000035 AE scasb ; set FLAGS like cmp '0', [rdi] would, if it was encodeable 31 00000036 10D2 adc dl,dl ; DL = DL<<1 | CF, rotate CF into DL. 32 00000038 E2FB loop .loop 33 0000003A 8D4281 lea eax, [rdx-127] ; no shorter than sub dl, 127 or xchg eax,edx / sub al,127 34 0000003D C3 ret We just do base 2 ASCII->integer conversion for 9 bits, letting the first bit (sign bit) overflow off the top of DL. The 8-bit return value is only the 8-bit AL register, as a 2's complement binary integer. adc dl,dl to rotate CF into DL is more efficient on modern CPUs than rcl dl, 1 (rotate through carry, i.e. shift in CF). They're equivalent except for some FLAGS output semantics. I could have used EDX for the same code size; that would simply leave different garbage in the high bytes outside the AL return value in RAX. (And yes, high garbage above narrow return values is 100% allowed by standard x86 calling conventions.) Try it online! 3 versions with a test caller. (Including alternate 15-byte scalar version using lodsb / and al, 1 / lea edx, [rdx*2 + rax]) x86-64 SIMD (MMXext+MOVBE) machine code, 20 bytes Having base-2 digits in MSB-first printing order is a major inconvenience for little-endian x86, where the lowest SIMD vector element comes from the lowest address in memory. x86 lacks a bit-reverse like ARM's rbit, so I used x86-64 features to byte-reverse 8 bytes. Without that, movq mm0, [e/rdi+1] (4B) / pslld mm0,7 (4B) would be 32-bit-mode compatible and work on a Pentium III (pmovmskb r32, mm was new with SSE, aka MMXext, so not original P5 Pentium-MMX). ; input: char *RDI; result int8_t AL ; clobbers: MM0. Leaves FPU in MMX state (no EMMS) 12 getexp_mmx: 14 00000010 480F38F04701 movbe rax, [rdi+1] ; byte-reverse (big-endian) load of the exponent field. saves 1B vs. mov + bswap 15 00000016 48C1E007 shl rax, 7 ; low bit to high in each byte. same size as pslld mm0, 7. 16 0000001A 480F6EC0 movq mm0, rax 17 0000001E 0FD7C0 pmovmskb eax, mm0 ; pack vector high bits into 1 byte 18 00000021 2C7F sub al, 127 19 00000023 C3 ret ; size = 0x24 - 0x10 = 0x14 = 20 B MMX instructions typically need 1 fewer prefix byte than SSE2 and later; if you were doing this for real you'd probably use XMM0, and avoid the EMMS (exit MMX state) that real code would normally use. (And maybe a pshufb for the byte reverse if you have SSSE3, to avoid bouncing through integer regs...) This should still be much faster than a loop with adc and false dependencies, and the slow loop instruction itself. It's handy that the exponent field is exactly 8 bits wide. Doing this for double would need an SSE2 16-byte load (or 2 byte reverses and annoying shuffles to combine into a 16-byte vector), and would have to mask away the sign bit. lodsb (1) + lodsq (2) + bswap rax (3) is the same total length as movbe rax, [rsi+1] (6), although that would make this portable to machines without movbe (Atom, Haswell and later). For subnormal (aka denormal) floats including 0.0, this just returns -127, the unbiased the exponent field, as requested by the question. And -128 (overflowed from +128) for Inf / NaN inputs. (I only realized this overflow issue after the fact, posting anyway because all 255 possible outputs are unique, and that puts both special exponents at adjacent values so the caller can test for subnormal or inf/nan with e <= -127.) It doesn't return the actual floor(log2(|x|)) of the represented value like AVX512 vgetexpps xmm, xmm does: (-Inf for an input of 0, and values below -127 for non-zero subnormals, matching the pseudocode in the documentation). So even if we could take input as an actual binary floating-pointer number in IEEE754 binary32 format (aka float) in an XMM register, we couldn't just use that one 6-byte AVX512 instruction to produce an integer-valued float result. not competing AVX2 version, 15 bytes This takes its arg as 32 bytes of ASCII digits in a YMM register, but they have to be in least-significant-digit first order, hence not competing. 2 getexp_avx2_ymm_reversed: 3 ; input: LSD-first ASCII digits in ymm0. Probably can't justify that and would need to byte-reverse 5 00000000 C5FD72F007 vpslld ymm0, ymm0, 7 6 00000005 C5FDD7C0 vpmovmskb eax, ymm0 7 00000009 C1E817 shr eax, 23 ; AL = exponent 8 0000000C 2C7F sub al, 127 9 0000000E C3 ret Fun fact: AVX-512 allows vpslld ymm0, [rdi], 7, if you had bytes in the LSD-first order in memory. (7 bytes including 4-byte EVEX, so an extra 2 bytes to take a char* instead of YMM arg.) MathGolf, 7 bytes ╞8<å♣(- Try it online. Explanation: ╞ # Remove the first character of the (implicit) input-string 8< # Slice to only keep the first 8 characters of the string å # Convert it from a binary-string to a base-10 integer ♣ # Push constant 128 ( # Decrease it by 1 to 127 - # Subtract it from the integer # (after which the entire stack joined together is output implicitly as result) JavaScript (ES6), 23 bytes f= b=>(0b{b}0>>>24)-127 <input oninput=o.textContent=f(this.value)><pre id=o> Uses the fact that >>> truncates to 32 bits. 05AB1E, 8 7 bytes ¦8£CƵQ- Try it online! ¦8£CƵQ- ¦ remove first element 8£ take next 8 elements C convert from binary string to an integer ƵQ push 127 - subtract Thanks to Kevin for -1 byte • Ah, you beat me to it. Was about to post my answer, lol. The žy< can be ƵQ. See the How to compress large integers? section in this tip of mine. Nov 5 '20 at 10:14 • This is still 7 bytes, not 6 Nov 5 '20 at 16:58 • @cairdcoinheringaahing lol too tired for this... Nov 5 '20 at 19:59 Ruby-n, 24 23 20 bytes -1 byte thanks to Sisyphus -3 bytes using -n command-line flag, inspired by this answer p_[1,8].to_i(2)-127 Try it online! Bash, 22 bytes echo [2#{1:1:8}-127] Try it online! Bash, 37 27 bytes Input is from STDIN, output is to STDOUT. expr [2#cut -b2-9] - 127 Try it online! Explanation expr # Evaluate the following: cut -b2-9 # Slice all characters from the 2nd character # to the 9th character in standard input [2# ] # Convert it from base 2 - 127 # And subtract by 127 • welcome, new contributor! ~~oh wait~~ Nov 15 '20 at 4:46 • Nov 30 '20 at 11:27 Kotlin, 26 31 29 bytes Converted to a valid anonymous function, thanks to Bubbler -2 bytes thanks to user {it.slice(1..8).toInt(2)-127} Try it online! • s is not defined here. You can do s=>s.slice or {it.slice instead – user Nov 5 '20 at 18:17 • @user - s is defined, and it's working! Any errors on your side? Nov 6 '20 at 3:08 • You need to include the part that defines s to be a function parameter in the byte count. Otherwise it is invalid. Nov 6 '20 at 4:34 • This is a valid lambda function. Nov 6 '20 at 6:43 • As user mentioned, this also works, where it is an implicit single parameter of a lambda. Nov 6 '20 at 8:01 Desmos, 42+9=5140+9=49 30 bytes f(l)=\sum_{i=0}^7l[9-i]2^i-127 Input as a list of 1's and 0's. Try It On Desmos! • Always love Desmos creations! Nice Nov 14 '20 at 12:59 • @MaximilianRose Thanks! I spend quite a lot of time using Desmos so I thought I would just try my skills at code golf using Desmos. Nov 15 '20 at 2:56 • I use to spend a lot of time on desmos, that's cool. Never even thought about shortcuts like "total" before. Dec 4 '20 at 2:27 APL (Dyalog Unicode), 12 bytes (SBCS) ¯127+2⊥8↑1↓⊢ Try it online! Retina 0.8.2, 63 bytes 2=M&!.{8} 1 01 +10 011  -127* (1*)-\1 - ^0*(-)?(1*)-? 1.2 Try it online! Explanation: 2=M&!.{8} Get the second overlapping 8-character substring. 1 01 +10 011 Convert to binary (may leave leading 0s).  -127* (1*)-\1 - Subtract 127 (may leave trailing -). ^0*(-)?(1*)-? 1.2 Convert to decimal, removing garbage. J, 12 bytes 127-~1{8#.$

Try it online!

#.        convert to binary
8  \       all the infixes with length 8
]      of the argument
1{           take the second one (0-indexed)
127-~             subtract 127

C#, 12011542 32 bytes

s=>Convert.ToByte(s[1..9],2)-127

Thanks to @SunnyMoon for realising that the code is not actually 120 bytes (-2)
Thanks to @Sunnymoon for slaying 3 sneaky spaces in the code (-3)
Thanks to @user for coding a much much shorter lambda solution (-73)
Thanks to @thedefault. for using the .. operator and Convert.ToByte (-10)

Try it online!

As it turns out, C# is actually quite useful for golfing! Lambdas are the way to go!

Welcome to code golf, Maximilian Rose!

• C# does lambdas, right? That would save on all the I/O overhead at least.
– Neil
Nov 5 '20 at 16:04
• You can actually substantially decrease it using lambdas (42 bytes). You also had a few spaces here and there.
– user
Nov 5 '20 at 17:20
• Well, you're now beating Retina...
– Neil
Nov 5 '20 at 20:27
• 33 bytes by changing the compiler and using a range: Try it online!. I also think you can use Convert.ToByte instead of Convert.ToInt32. Nov 6 '20 at 5:04
• Thanks for the welcome Nov 10 '20 at 6:19

Python 3, 54 bytes

ty to @Bubbler and @Kevin Cruijssen for corrections

lambda n: sum([2**i*int(n[8-i])for i in range(8)])-127

Try it online!

• Answers must be a function (taking input from function parameter) or a full program (taking input from stdin), i.e. you can't assume that the input is already stored in a variable. You can convert it to an anonymous lambda instead. Nov 5 '20 at 9:57
• I generally recommend waiting a week before answering your own challenge, especially if you do so in a popular language like Python. This is to give others a chance to discover and think about the task. You obviously know the game is on, and you've already spent time thinking about it.
Nov 5 '20 at 9:59
• You can remove the space at )for and the 0, in the range(8) (after converting it to a valid lambda as mentioned by @Bubbler). Nov 5 '20 at 13:53
• @Adám That's a big meh. You can just ignore the answer. Having an answer doesn't stop anyone from thinking about the task. To me, that type of policing goes far beyond the spirit of Stack Exchange. Nov 7 '20 at 12:06
• @Cody Gray and anyway I was so caught up with converting the solution I used in the actual exam which was psudocode without base conversion functions that I completely forgot about int(n, 2) to change base, which let another person find a shorter solution, I won't remove this one because I'm still proud of finding a nice use of list comprehension but I didn't expect it to be upvoted to the top of anything Nov 7 '20 at 12:09

><>, 30 bytes

r~8[01:@{2%*+$2*60l4)?.{""-n; (contains an unprintable DEL between the quotes) Try it Online! r~8[ grab relevant part of string 01 accumulator and exponent initialization main loop @{2% convert current digit charcter to 0 and 1 *+$                 accumulate
:       2*               increment exponent
60l4)?.        loop while digits remaining
{""-n;  print result sub 127 and terminate

Most of the work is constructing a number from binary notation.

Scala, 27 bytes

Uses Lynn's clever approach

_.slice(1,9)reduce(_*2+_-1)

Try it online

Scala, 29 bytes

n=>BigInt(n.slice(1,9),2)-127

Try it online!

Very straightforward. Takes a String, parses the exponent's bits as an integer in binary, then subtracts 127, returning a BigInt.

Scala, 29 bytes

n=>(BigInt(n.tail,2)>>23)-127

Try it online

Unfortunately, because of operator precedence, the outer parentheses are needed.

Scala, 29 bytes

_.slice(1,9)./:(0)(_*2+_)-127

Try it online

Yet another solution, this time taking a list of digits (List[Int]).

Forth (gforth), 49 bytes

: b drop 1+ 8 ['] evaluate 2 base-execute 127 - ;

Try it online!

Done using (a lot of) Bubbler's help.

Forth has one(1) convenient command that allows interpretation of strings as binary, and that's used here(base-execute).

Rust, 43 bytes

Try it online!

Returns a Result<i32,ParseIntError>. I would have loved to use an i8 instead of an i32 but that would panic if the leading bit is 1.

• Is there no u8 type?
– Neil
Nov 22 '20 at 16:46
• @Neil there is an i8 type but the code will panic if s[1] == 1 Nov 22 '20 at 20:14
– Neil
Nov 22 '20 at 21:04
• @Neil There is a u8 type but I would have to cast it into a different type to prevent underflow if s[1] == 0, and casts are lots of bytes Nov 23 '20 at 1:50
• Oh, right, it will try to do an unsigned subtraction? I guess I'm too used to C's default int promotion. Sorry about that.
– Neil
Nov 23 '20 at 10:30

brainfuck, 174113 111 bytes

-61 bytes for many optimizations

-2 bytes thanks to RezNesX

,>++++++++>>--[>-<--]>-<<<[>,>>>[--<+>]<[->+<]<-[<->-----]<+++[->>>[->+>+<<]>>[-<<+>>]<<<<<]<-]>>>>---[>-<--]>.

Try it online!

Input is 32 bits taken one at a time.

Explanation:

,> first bit garbage
++++++++ set cur cell (cell 2) to 8
>>--[>-<--]>-<<< set cell 5 to 128 (so the first division moves it over)

[> do 8 times

, input to cell 3

>>> cell 6
[--<+>] div 2 to 5
<[->+<] move back to 6
< cell 4

-[<->-----]<+++ convert to bin value (sub 48) using cell 4

[- if 1 (in cell 3)
>>>[->+>+<<] add cell 6 into cell 7 & 8
>>[-<<+>>] move cell 8 back to 6
<<<<<
]

<-] ends on c2

>>>> to c6 (has 1 and next cell has value)

---[>-<--]> sub 127
. outputs final answer (as ASCII= if this is invalid let me know)
• 111 bytes Feb 18 at 8:52

Vyxal, 11 6 bytes

9ŻB⁺ɽ-

Try it Online!

I've got an advantage that y'all don't have: I actually sat the exam this question is based upon as well at the same time as the OP (#hsc_gang #option_topic_2 #ir_sw_hw). Hopefully I got both that question and this answer right!

Explained

9ŻB⁺ɽ-
9Ż      #     input[1:9]
B     # int(          , 2)
⁺ɽ   #                      127 (N.B. Pushes the index of ɽ in the codepage + 101)
-  #                    -
# int(input[1:9], 2) - 127

Update: Turns out I got the wrong answer in the exam. Not pog.

• finally, a challenge where Vyxal doesn't smash everything in Nov 15 '20 at 3:35
• No tail operator or prefix slicing!? (Or, a built-in for 127.)
– user99151
Nov 15 '20 at 4:14
• @Razetime what were you saying? Nov 15 '20 at 5:06
• b r u h m o m e n t o Nov 15 '20 at 5:12

Jelly, 8 bytes

Ḋḣ8Ḅ_127

Try it online!

Takes input as a list of digits

How it works

Ḋḣ8Ḅ_127 - Main link. Takes a list of bits B on the left
Ḋ        - Dequeue; Remove the first element of B
ḣ8      - Take the first 8 elements
Ḅ     - Convert from binary
_127 - Subtract 127

K (ngn/k), 11 bytes

-127+2/8#1_

Try it online!

1_        drop one
8#          take 8
2/            to binary
-127+              subtract 127

{y-1+2*x}/8#1_

Try it online!

Try it online!

import strutils

Try it online!

Charcoal, 13 bytes

Ｉ⁻⍘Φ…Ｓ⁹κ²¦¹²⁷

Try it online! Link is to verbose version of code. Explanation:

Ｓ          Input string
… ⁹         Take first 9 characters
Φ   κ        Remove first character
⍘     ²       Convert from base 2
⁻        ¹²⁷   Subtract 127
Ｉ               Cast to string
Implicitly print

Canvas, 13 bytes

［］９＠ｊ｛┤］２┴‾p－

Try it here!

Explanation

[]9@j{⊣]2⊥¯p⊣-
[]              get all prefixes
9@            9th prefix
j           remove first element
{⊣]        cast each element to integer
2⊥      decode from base 2
¯p    127
-   subtract that from it

JavaScript (Node.js),  26  24 bytes

x=>'0b'+x.slice(1,9)-127

Try it online!