12
\$\begingroup\$

Input a scientific notation number (base 10), output scientific notation in base 16 (as defined below).

Details

In scientific notation, all non-zero numbers are written in the form

$$ m \times 10^n $$

Where \$ n \$ is an integer, and \$ m \$ is a real number, \$ 1 \leq |m| < 10 \$.

Consider scientific notation in base 16.

$$ m \times 10^n = m' \times 16^{n'} $$

\$ n' \$ is an integer, and \$ m' \$ is a real number where \$ 1 \leq |m'| < 16 \$.

Input / Output

Input a positive real number. You may also choice to input \$m\$, and, \$n\$ separately. For all testcase, -100 < n < 100.

Output the number in hexadecimal scientific notation. Could be a single string or two strings. Number \$m\$, and, \$n\$ should also be formatted as hexadecimal strings.

Output as 1.2E3E4 is not allowed due to ambiguous. (1.2E3×104, or 1.2×103E4) You have to use other notations. For example, 1.2E3E+4, 1.2E3, 4, 1.2E3&4, 1.2e3E4, 1.2E3e4, 1.2E3P4, 1.2E3⏨4, 1.2E3*^4 are all acceptable.

Testcases

m, n -> m', n'
1.6, 1 -> 1, 1
6.25, -2 -> 1, -1
1.0, 1 -> A, 0
7.257672195146994, 93 -> d.eadbeef, 4d
1.234567, 89 -> f.83e0c1c37ba7, 49
1, -99 -> 8.bfbea76c619f, -53

You output may be slightly different from given testcase due to floating point errors. But you should keep at least 4 hex digits precision, and \$1 \leq m' < 16\$.

Rule

This is code golf. Shortest codes in each languages win.

\$\endgroup\$
6
  • \$\begingroup\$ @Noodle9 In 1.2E3E4, the digit E and the exponential separator have the same case. \$\endgroup\$
    – Arnauld
    Commented Jun 26, 2020 at 12:57
  • \$\begingroup\$ @Noodle9 The E in 1.2E3 is not an exponent, it is hexadecimal 14. So in the examples n' is not 1.2*10^3 (1200), it is 1 + 2/16 + 14/16*16 + 3/16*16*16 (approximately 1.1804 decimal). \$\endgroup\$ Commented Jun 26, 2020 at 21:28
  • \$\begingroup\$ @2012rcampion Ahhhhh! Now it makes total sense - thanks! :D \$\endgroup\$
    – Noodle9
    Commented Jun 26, 2020 at 21:31
  • 1
    \$\begingroup\$ Be good to add a negative testcase. \$\endgroup\$
    – Noodle9
    Commented Jun 26, 2020 at 22:34
  • \$\begingroup\$ Since there are already some answers assume positive numbers. I'm going to restrict the input as positive integers. Sorry for any changes. I had updated the description which only require to handle positive numbers. \$\endgroup\$
    – tsh
    Commented Jun 27, 2020 at 3:10

8 Answers 8

4
\$\begingroup\$

JavaScript (ES6),  134 124  121 bytes

Expects a float and returns an array of 2 strings.

n=>([x,y]=(+(g=n=>s=n.toString(16))(n).replace(r=/[1-f]/g,1)).toExponential().split`e`,[x.replace(r,_=>r.exec(s)),g(+y)])

Try it online!

How?

We first convert the input to hexadecimal and save the result in the variable s.

For instance, 7.257672195146994e93 is turned into:

"deadbeef0000000000000000000000000000000000000000000000000000000000000000000000"

We replace all non-zero hexadecimal digits with 1's:

"111111110000000000000000000000000000000000000000000000000000000000000000000000"

We coerce this back to an integer and invoke the .toExponential() method:

"1.1111111e+77"

We split this string into x = "1.1111111" and y = "+77".

We replace all 1's in x with the non-zero hexadecimal digits of s in order of appearance:

"d.eadbeef"

Finally, we convert y to hexadecimal:

"4d"

Below is another example with 6e-19:

"0.000000000000000b116b7de48f008"
"0.00000000000000011111111111001"
"1.1111111111001e-16"
[ "1.1111111111001", "-16" ]
[ "b.116b7de48f008", "-10" ]

Commented

n => (                       // n = input
  [x, y] =                   // x = mantissa, y = exponent
    (                        //
      +(                     // coerce to integer:
        g = n =>             //   g is a helper function converting its input ...
          s = n.toString(16) //     ... to a hexadecimal string saved in s
      )(n)                   //   invoke g with n
      .replace(              //   replace:
        r = /[1-f]/g,        //     r = regular expression to match the non-zero
                             //         hexa digits
        1                    //     replace all of them with 1's
      )                      //   end of replace()
    )                        //
    .toExponential()         // convert to exponential notation 
    .split`e`,               // split into [ x, y ] = [ mantissa, exponent ]
  [                          // output array:
    x.replace(               //   replace in x:
      r,                     //     use r a 2nd time to match the 1's
      _ => r.exec(s)         //     use r a 3rd time to get the next hexa digit
                             //     from s, this time taking advantage of the
                             //     stateful nature of RegExp
    ),                       //   end of replace()
    g(+y)                    //   convert y to hexadecimal
  ]                          // end of output array
)                            //
\$\endgroup\$
1
\$\begingroup\$

JavaScript (ES6), 217 bytes

f=
n=>/^-?0\./.test(n=n.toString(16))?n.replace(/^(-?)0(.0*)(.)(.*)/,(_,s,z,d,t)=>s+d+'.'+t+'e-'+z.length.toString(16)):n=n.replace(/(-?.)(\w*).?(.*)/,(_,s,d,t)=>s+'.'+d+t+'e='+d.length.toString(16)).replace(/0*e=/,"e+")
<input type=number step=any oninput=o.textContent=f(+this.value)><pre id=o>

Output format is -?[1-f]\.([0-f]*[1-f])?e[+-][1-f][0-f]*.

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

Perl 5, 181 bytes

sub f{($e,$x)=(0,10**pop()*pop);$x/=16,$e++while$x>=16;$x*=16,$e--while$x<1;join('',map{sprintf$_?'%x':'%x.',$x%16,$x-=$x%16,$x*=16}0..12)=~s,\.?0*$,,r,sprintf$e<0?'-%x':'%x',abs$e}

Try it online!

I think using sprintf('%a',$x) could make the answer much shorter, just not sure how.

Ungolfed:

sub f {
  my($m, $n) = @_;
  my($e, $x) = (0, $m * 10**$n);
  $x/=16, $e++ while $x >= 16;
  $x*=16, $e-- while $x <  1;
  return (
    join('',map{sprintf$_?'%x':'%x.',$x%16,$x-=$x%16,$x*=16}0..12) =~ s,\.?0*$,,r,
    sprintf($e<0?'-%x':'%x',abs$e)
  )
}

Test:

for my $test (map[/-?[\da-f\.]+/gi],split/\n/,<<''){
    1.6, 1                -> 1, 1
    6.25, -2              -> 1, -1
    1.0, 1                -> a, 0
    7.257672195146994, 93 -> d.eadbeef, 4d
    1.234567, 89          -> f.83e0c1c37ba7, 49
    1, -99                -> 8.bfbea76c619f, -53

    my($m,$n,$Mexp,$Nexp)=@$test;
    my($Mgot,$Ngot)=f($m,$n);
    my $testname = sprintf"  %-25s -->  %s", "$m, $n", "$Mexp, $Nexp";
    is("$Mgot,$Ngot", "$Mexp,$Nexp", $testname);
}

Output:

ok 1 -   1.6, 1                    -->  1, 1
ok 2 -   6.25, -2                  -->  1, -1
ok 3 -   1.0, 1                    -->  a, 0
ok 4 -   7.257672195146994, 93     -->  d.eadbeef, 4d
ok 5 -   1.234567, 89              -->  f.83e0c1c37ba7, 49
ok 6 -   1, -99                    -->  8.bfbea76c619f, -53
\$\endgroup\$
1
\$\begingroup\$

Ruby, 62 61 bytes

->x{'%x.%x,%+x'%[m=x/16**n=Math.log(x,16).floor,m%1*16**9,n]}

Try it online!

Input is a (decimal) float. Output is in the form \$m',n'\$, where \$m'\$ has a maximum precision of 10 hex digits and \$n'\$ is always signed.

'%x.%x,%+x' is a shorthand form of sprintf syntax, which creates formatted strings for numeric output. The format specifier x converts its argument to hexadecimal and the + guarantees signed output (otherwise negative numbers would be output with two leading dots representing an infinite string of leading ffs). We do three conversions to hex: (i) the integral part of \$m'\$, (ii) the fractional part of \$m'\$ (m%1; multiplying by 16**9 is necessary because the fractional part is ignored by sprintf), and (iii) \$n'\$.

The solution makes use of some straightforward mathematical transformations. Let \$m'=16^{m''}\$, so that \$x\equiv m10^n=m'16^{n'}=16^{m''+n'}\equiv 16^y\$. Then \$y=\log_{16}x\$. We are told that \$n'\$ is an integer, hence we take \$n'=\lfloor y\rfloor\$. This is the only choice of \$n'\$ for which \$0\le m''=y-n'<1\$, and hence the only choice of \$n'\$ for which \$1\le 16^{m''}=m' < 16\$ as required.

To support negative inputs, add .abs in two places and another + in the format string, bringing the code to 70 bytes:

->x{'%+x.%x,%+x'%[m=x/16**n=Math.log(x.abs,16).floor,m.abs%1*16**9,n]}
\$\endgroup\$
1
\$\begingroup\$

C(GCC), 32 bit float, 133 129 128 bytes

-4 bytes ceilingcat

m;e;s(float f){m=*(int*)&f;e=(m>>23)-127;m=(m&-1U>>9|1<<23)>>3-(e&3);printf("%x.%05xE%c%x",m>>20,m&-1U>>12,"+-"[e<0],abs(e/4));}

Try it online!

This extracts the exponent and mantissa from a floating point number. Since the maximum exponent is +/-127 in base 2 (approx 38 base 10), this doesn't quite meet the challenge since it fails on larger exponents. So...

C(GCC), 64 bit float, 167 163 147 bytes

-4 bytes ceilingcat

long m;e;s(double f){m=*(long*)&f;e=(m>>52)-1023;m=(m&-1UL>>12|1L<<52)>>3-(e&3);printf("%lx.%013lxE%c%x",m>>49,(m&-1UL>>15)*8,"+-"[e<0],abs(e/4));}

Try it online!

\$\endgroup\$
1
  • \$\begingroup\$ Suggest (~e&3) instead of 3-(e&3) \$\endgroup\$
    – ceilingcat
    Commented Jul 11, 2020 at 5:12
1
\$\begingroup\$

R, 192 bytes

function(x,i=function(x,p=F,y=abs(x))`if`(y>0,{d=c(0:9,letters[1:6])[rev(y%/%(16^(0:log(y,16)))%%16+1)]
c("-"[x<0],d[1],"."[p],d[-1])},0))cat(i(x*16^(3-(n=log(x^2,16)%/%2)),T)," ",i(n),sep="")

Try it online!

Happily handles negative numbers, even if now not required.

Commented:

base16float==function(x,                                    
    l=c(0:9,letters[1:6]),                          # define hexadecimal digits
    i=function(x,point=FALSE,y=abs(x))              # create function to write hexadecimal integers
    `if`(x==0,0,                                    # if x is zero, just write zero
        {d=l[rev(y%/%(16^(0:log(y,16)))%%16+1)]     # otherwise get the digits for each power-of-16
            c("-"[x<0],d[1],"."[p],d[-1])})         # and paste them together with the sign
    )                                               # (and with a "." after the first digit if 
                                                    # specified by point=TRUE in the function call)
    cat(i(x*16^(3-(n=log(abs(x),16)%/%1)),T),       # so: first output the mantissa as a 4-digit integer
                                                    # with point=TRUE to include the dot,
        " ",                                        # leave a gap,
        i(n),                                       # and write the exponent
        sep="")
\$\endgroup\$
0
\$\begingroup\$

Python 3.8, 284 bytes

struct approach. At least I tried)

from struct import*
m=lambda s,c,n,k:[s[n:],'-'+s[k:]][c]
n=lambda s:m(s,s[0]=='-',2,3)
o=lambda s,x:m(s,x<0,0,0)
p=lambda x:x[2]+'.'+x[3:]
s=lambda x,y,d=2**52:(o(p(hex((x%d+d)*2**((x//d+1)%4)).rstrip('0')),y),n(hex(((x//d)%2048-1023)//4)))
f=lambda x:s(unpack('Q',pack('d',x))[0],x)

Try it online!

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

Python 3, 135 \$\cdots\$ 119 105 bytes

def f(x):m,n=x.hex().split('p');m=hex(int('1'+m[4:],16)<<int(n)%4);return m[2]+'.'+m[3:],f'{int(n)//4:x}'

Try it online!

Inputs a float.
Returns a tuple of strings \$(m',n')\$.

\$\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.