joncampbell123 / dosbox-x

DOSBox-X fork of the DOSBox project
GNU General Public License v2.0
2.67k stars 378 forks source link

Intermittent floating point errors with program using Borland C++ 3.1 floating point emulation #2233

Open Damaniel opened 3 years ago

Damaniel commented 3 years ago

Describe the bug

I'm working on a game that uses a very small amount of floating point math, built with Borland C++ 3.1. I noticed intermittent problems with the floating point math generating exceptions in my code, and after tracing it down, it seems like any application I build using Borland C++ 3.1's floating point emulation library intermittently fails in DOSBox-X but not elsewhere.

To Reproduce Steps to reproduce the behavior:

  1. Using Borland C++ 3.1, create a short program that performs some floating point math. In my case, I reduced the problem to a loop that goes from 0 to 20, and does the division (x/20), printing the result. The program is listed below.
  2. Compile the program using the default floating point settings (which is using fast floating point emulation). Just 'bcc test.cpp' will work. The same issue will occur with the full IEEE FP emulation library (i.e. passing the '-ff-' flag to the compiler)
  3. Run the program a few times. For me, the number of times seems to average 5-10 times, but can take a few dozen times to happen.
  4. After displaying correct results for the first few runs, the result on the first 'incorrect' run will be very incorrect (generally 0.000000 or -0.000000 for each iteration of the loop)
  5. The subsequent run will also be wrong, but then follow-on runs will be correct - until they're not again.

Expected behavior The floating point calculations should always work correctly.

Screenshots

A correct run: https://i.imgur.com/5SWW51Z.png

An incorrect run: https://i.imgur.com/snzfWPM.png

Environment:

Additional context

I've tested this program in the following places and confirmed that the behavior in these places is correct:

If you configure Borland C++ to use hardware floating point instructions, the problem goes away - it only appears when using Borland's floating point emulation library (either the fast or accurate version). I've tried adjusting cycle settings to confirm whether the problem may be CPU speed related, but I can reproduce it all the way down to ~5000cycles/ms (I haven't tried lower).


Example program:

#include <stdio.h>

int main(void) {
    float f;

    for (int i = 0; i < 20; i++) {
    printf("%d/20 = %f\n", i, (float)(i/20.0));
    }

    return 0;
}

dosbox-x.conf

[sdl]
autolock=true

[dosbox]
title=DOS
memsize=64
vmemsize=4
machine=svga_paradise
vesa modelist width limit=0
vesa modelist height limit=0

[dos]
ver=7.1

[cpu]
cputype=pentium
core=dynamic_x86
cycles=max

[sblaster]
sbtype=sb16
irq=5
blaster environment variable = true

[ne2000]
# If you want networking in Windows, set ne2000=true.
# This also requires that you set realnic= to a suitable value for your PC
#ne2000=true
#nicirq=10
#realnic=Realtek

#[fdc, primary]
int13fakev86io=true

#[ide, primary]
int13fakeio=true
int13fakev86io=true

#[ide, secondary]
int13fakeio=true
int13fakev86io=true
cd-rom insertion delay=4000

[render]
scaler=none
aspect=true

[autoexec]
mount C C:\dosbox-x\hd
set PATH=C:\windows;C:\BORLANDC\BIN;%PATH%
C:
Wengier commented 3 years ago

Thanks for reporting this! I have checked it, and I think the issue is with the core setting in the config file. The problem seems to only happen if you set core=dynamic_x86 but not core=dynamic_rec or core=normal. So please try to set the latter two (core=dynamic_rec or core=normal) instead of the dynamic_x86 core in this case for now. Hope this helps.

P.S. I have tried the same setting (dynamic_x86 core) in DOSBox SVN and ECE which will lead to a crash and exit instead of giving incorrect result. DOSBox 0.74 will also give incorrect result if the dynamic_x86 core is active.

grapeli commented 3 years ago

@Wengier Same code compiled and running under linux gives the correct result with core=dynamic_x86. linux.zip

dosbox-x -set cputype=pentium -set core=dynamic_x86 -set cycles=max -set "cpu use dynamic core with paging on"=false -c "mount c ./linux.zip" -c "c:" -c "linux.bat"

Wengier commented 3 years ago

@grapeli Yes, but I think /usr/bin/example is a Linux executable running in Linux (within DOSBox-X) rather than DOS executable running directly in DOSBox-X in this case, and so different results may be somehow expected.

P.S. With DOSBox-X 0.83.10 you can just type the command as the following in this case:

dosbox-x ./linux.zip -set cputype=pentium -set core=dynamic_x86 -set cycles=max -c "c:" -c "linux.bat"

Note that dosbox-x ./linux.zip is functionally equivalent to dosbox-x -c "mount c ./linux.zip", and with the default cpu use dynamic core with paging on=auto it is enabled if and only if the 386 paging AND the guest system are enabled.

yksoft1 commented 3 years ago

2395 might also be related since PC-98 Touhou games are known to use Borland C++ CRT.

joncampbell123 commented 3 years ago

Some builds cannot provide the full 80-bit IEEE floating point precision of Intel processors. These builds include the VS2019 builds and any non-Intel builds.

I'm currently aware of several test cases in my collection that have issues when less than 80-bit precision is available: