- asinh() is not in IEEE754, please ignore my comment about
non-conformity, sorry.
- The calculation for asinh() is pretty badly conditioned, i.e.
it blows up errors in the basic calculations.
- asinh() is implemented in glibc in assembler on the FPU stack.
This would mean 80 bits float representation.
Error observations:
Calculations for asinh( -2.1073424255447017e-08), derived from
the Python 3.1.2 selftest. Formula is asinh(x) = log(x+sqrt(x*x+1))
Reference is a calculation with long double (128 bit)
- qemu: 6 correct digits behind the dot
- C with double: 7 correct digits behind the dot
- i387: 10 correct digits behind the dot
Possible root cause:
The observed error may be due to qemu using 64 bit double math in
the FPU implementation, instead of the 80 bit internal precision
a real i387 uses.
Comparison with kernel i387 emulation
As an additional verification, I tried the calculations using the
Linux kernel coprocessor emulation. This basically failed. With
kernel 2_6_26 and 2_6_27 and the ''no387'' flag, the kernel locked
up without output early during boot. With 2.6.34, I could not do
an ssh login. I may try this again later, and especially try
to find out what is wrong with qemu and 2.6.34.
Consequences:
1) qemu is significantly less accurate for some mathematics than a
genuine i387. This may cause problesm with sensitive mathematics
developed on a real i387.
2) qemu can be fingerprinted easily using this problem. This may
have security implications.
Fix Options:
1) - Moving the FPU internal representation to long double. It will
still not be the same results as on an i387, but it will be
_more_ accurate instead of less, which generally is far less
of a problem.
Pro: Higher accuracy
Cons: Slower
- Use code derived from the Linux kernel i387 FPU emulator.
The emulator is not bit-exact, but relatively close and it
is used at least on some small x86 architectures.
Pro: Higher accuracy
Cons: Need to integrate foreign code
- Leave it as it is but add a clear warning to the "Known qemu
Problems" Section in the manual and Wiki (I did not find one, I
think it should be added in an easily findable place), that
i387 FPU emulation is only 64 bits internally and may be less
exact for combined calculations done on the FPU stack, such
as asinh() in glibc(), than a real i387.
You may recommend to use kernel FPU emulation, but
this may not work or be a pain to get working.
Pro: Least work
Cons: Not really a solution
2) Fixing this is difficult and there are other ways to find
out that you are running in qemu anyways. I think the security
implications are insiginficant in most cases.
And I unexpectedly had more time to dig.
Findings:
- asinh() is not in IEEE754, please ignore my comment about
non-conformity, sorry.
- The calculation for asinh() is pretty badly conditioned, i.e.
it blows up errors in the basic calculations.
- asinh() is implemented in glibc in assembler on the FPU stack.
This would mean 80 bits float representation.
Error observations:
Calculations for asinh( -2.107342425544 7017e-08) , derived from
the Python 3.1.2 selftest. Formula is asinh(x) = log(x+sqrt(x*x+1))
Reference is a calculation with long double (128 bit)
- qemu: 6 correct digits behind the dot
- C with double: 7 correct digits behind the dot
- i387: 10 correct digits behind the dot
Possible root cause:
The observed error may be due to qemu using 64 bit double math in
the FPU implementation, instead of the 80 bit internal precision
a real i387 uses.
Comparison with kernel i387 emulation
As an additional verification, I tried the calculations using the
Linux kernel coprocessor emulation. This basically failed. With
kernel 2_6_26 and 2_6_27 and the ''no387'' flag, the kernel locked
up without output early during boot. With 2.6.34, I could not do
an ssh login. I may try this again later, and especially try
to find out what is wrong with qemu and 2.6.34.
Consequences:
1) qemu is significantly less accurate for some mathematics than a
genuine i387. This may cause problesm with sensitive mathematics
developed on a real i387.
2) qemu can be fingerprinted easily using this problem. This may
have security implications.
Fix Options:
1) - Moving the FPU internal representation to long double. It will
still not be the same results as on an i387, but it will be
_more_ accurate instead of less, which generally is far less
of a problem.
Pro: Higher accuracy
Cons: Slower
- Use code derived from the Linux kernel i387 FPU emulator.
The emulator is not bit-exact, but relatively close and it
is used at least on some small x86 architectures.
Pro: Higher accuracy
Cons: Need to integrate foreign code
- Leave it as it is but add a clear warning to the "Known qemu
Problems" Section in the manual and Wiki (I did not find one, I
think it should be added in an easily findable place), that
i387 FPU emulation is only 64 bits internally and may be less
exact for combined calculations done on the FPU stack, such
as asinh() in glibc(), than a real i387.
You may recommend to use kernel FPU emulation, but
this may not work or be a pain to get working.
Pro: Least work
Cons: Not really a solution
2) Fixing this is difficult and there are other ways to find
out that you are running in qemu anyways. I think the security
implications are insiginficant in most cases.