80386 Assembly (41 bytes - 15 = 26)
8B 4C 24 04 8B 54 24 08 D9 EA DC 09 D9 C0 D9 FC DC E9 D9 C9 D9 F0 D9 E8 DE C1 D9 FD DD 02 D9 FB D8 CA DD 19 D8 C9 DD 1A C3
Floating point math in assembly is a pain..
This is a function (cdecl calling convention) that calculates e^(a+bi)
via the relation e^(a+bi)=e^a cos b + e^a sin b * i
. (It accepts two double *
parameters, a and b in that order, and outputs the real part into the a
pointer, and the imaginary part into the b
pointer).
The function prototype in C would be:
void __attribute__((cdecl)) exp_complex(double *a, double *b);
About half (20, to be exact) of the 41 bytes were spent calculating e^a
; another 8 were spent getting the function arguments into registers.
The above bytecode was written by hand, from the following assembly (NASM-style, commented):
; get pointers into ecx, edx
mov ecx, [esp+4] ; 8B 4C 24 04
mov edx, [esp+8] ; 8B 54 24 08
; log(2,e)
fldl2e ; D9 EA
; a log(2,e)
fmul qword [ecx] ; DC 09
; duplicate
fld st0 ; D9 C0
; get integer part
frndint ; D9 FC
; get fractional part
fsub st1, st0 ; DC E9
; put fractional part on top
fxch st1 ; D9 C9
; 2^(fract(a log(2,e))) - 1
f2xm1 ; D9 F0
; 2^(fract(a log(2,e)))
fld1 ; D9 E8
faddp st1 ; DE C1
; 2^(fract(a log(2,e))) * 2^(int(a log(2,e)))
; = e^a
fscale ; D9 FD
; push b
fld qword [edx] ; DD 02
; sin and cos
fsincos ; D9 FB
; e^a cos b
fmul st2 ; D8 CA
; output as real part
fstp qword [ecx] ; DD 19
; e^a sin b
fmul st1 ; D8 C9
; output as imaginary part
fstp qword [edx] ; DD 1A
; exit
ret ; C3
To try this in C (gcc, linux, intel processor):
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
int main(){
// bytecode from earlier
char code[] = {
0x8B, 0x4C, 0x24, 0x04, 0x8B, 0x54, 0x24, 0x08,
0xD9, 0xEA, 0xDC, 0x09, 0xD9, 0xC0, 0xD9, 0xFC,
0xDC, 0xE9, 0xD9, 0xC9, 0xD9, 0xF0, 0xD9, 0xE8,
0xDE, 0xC1, 0xD9, 0xFD, 0xDD, 0x02, 0xD9, 0xFB,
0xD8, 0xCA, 0xDD, 0x19, 0xD8, 0xC9, 0xDD, 0x1A,
0xC3,
};
// allocate executable memory to a function pointer called 'exp_complex'
void __attribute__( (__cdecl__) ) (*exp_complex)(double *,double *) = mmap(0,sizeof code,PROT_WRITE|PROT_EXEC,MAP_ANON|MAP_PRIVATE,-1,0);
memcpy(exp_complex, code, sizeof code);
// test inputs
double a = 42.24, b = -2.7182818;
printf("%.07g + %.07g i\n", a, b);
// call bytecode as a c function
exp_complex(&a, &b);
printf("%.07g + %.07g i\n", a, b);
// release allocated memory
munmap(exp_complex, sizeof code);
return 0;
}
And J uses ajb. – Adám – 2016-08-04T19:38:02.560
Is N always an (imaginary) integer? – Adám – 2016-08-04T19:48:18.893
Can we print a double struck
i
, provided we don't actually use complex arithmetic in the code? – Martin Ender – 2014-11-25T14:08:44.817@mmk See the image in belisarius's comment on my Mathematica answer. May the
i
in the output look like that, or does it have to be ASCII code 0x69? – Martin Ender – 2014-11-26T02:19:07.077@MartinBüttner, as long as it isn't capitalized, it is OK. – mmk – 2014-11-26T02:26:56.623
what are acceptable in/out formats? A list of 2 numbers? a+bi? r theta? native representation (APL represent complex numbers as aJb)? – TwiNight – 2014-11-26T10:39:42.587