Linux Windows Floating Point Conversion Emulation

I ran into an annoying issue when releasing a floating point number. When I format 11.545 to the nearest 2 decimal places on Windows, it displays "11.55", as you would expect. However, when I do the same on Linux, the output is "11.54"!

I initially ran into a problem in Python, but further research showed that the difference is in the base C library of time. (The x86-x64 architecture in both cases.) Running the next line of C produces different results on Windows and Linux, like Python.

printf("%.2f", 11.545); 

To shed more light, I printed a number up to ten decimal places ( "%.20f" ):

 Windows: 11.54500000000000000000 Linux: 11.54499999999999992895 

I know that 11.545 cannot be stored exactly as a binary number. So, apparently, what happens is that Linux displays the number that it actually stores with maximum precision, while Windows displays the simplest decimal representation, i.e. trying to guess what the user most likely meant.

My question is: is there a (reasonable) way to emulate Linux behavior on Windows?

(Although the behavior of Windows is certainly intuitive, in my case I really need to compare the output of a Windows program with a Linux program, and Windows is the only one I can change. By the way, I tried to look at the source of Windows printf , but the actual function that performs the conversion string float-> _cfltcvt_l , and its source does not seem to be available.)

EDIT: the plot is thickening! The theory about this caused by an inaccurate representation may be wrong, since 0.125 has an exact binary representation and is still different when exiting with '%.2f' % 0.125 :

 Windows: 0.13 Linux: 0.12 

However, round(0.125, 2) returns 0.13 for both Windows and Linux.

+7
c python floating-point precision printf
source share
6 answers

First of all, it sounds like Windows has the right wrong in this case (not that it really matters). Standard C requires that the value displayed on %.2f be rounded to the appropriate number of digits. The most famous algorithm for this is dtoa , implemented by David M. Gay . You can probably port this to Windows or find your own implementation.

If you haven't read Stell and White's How to Print Floating-Point Numbers, find a copy and read it. This is certainly an educational reading. Remember to find the original from the late 70s. I think at some point I bought ACM or IEEE from me.

+2
source share

I don’t think that Windows is doing something particularly smart (for example, trying to reinterpret the float in base 10) here: I would assume that it just accurately calculates the first 17 significant digits (which will give β€œ11.545000000000000”) and then overlaying extra zeros on end to make up the required number of places after the point.

As others have said, different results for 0.125 come from Windows using round-half-up and Linux using round-half-to-even.

Note that for Python 3.1 (and Python 2.7, when it appears), the result of formatting the float will be platform independent (except, perhaps, on unusual platforms).

+2
source share

The decimal module gives you access to several rounding modes:

 import decimal fs = ['11.544','11.545','11.546'] def convert(f,nd): # we want 'nd' beyond the dec point nd = f.find('.') + nd c1 = decimal.getcontext().copy() c1.rounding = decimal.ROUND_HALF_UP c1.prec = nd d1 = c1.create_decimal(f) c2 = decimal.getcontext().copy() c2.rounding = decimal.ROUND_HALF_DOWN c2.prec = nd d2 = c2.create_decimal(f) print d1, d2 for f in fs: convert(f,2) 

You can build a decimal number from an int or string. In your case, feed a string with more digits than you want and truncate by setting context.prec.

Here is a link to a pymotw post with a detailed overview of the decimal module:

http://broadcast.oreilly.com/2009/08/pymotw-decimal---fixed-and-flo.html

+1
source share

Consider comparing floating point numbers with some tolerance / epsilon. This is much more reliable than trying to match exactly.

I mean, if not to say that the two floats are equal when:

 f1 == f2 

Say that they are equal if:

 fabs(f1 - f2) < eps 

For a little eps . More on this issue can be found here .

0
source share

You can try to subtract (or add for a negative number) a small delta, which will not affect rounding for numbers, far enough from accuracy.

For example, if you round with %.2f , try this version on Windows:

 printf("%.2f", 11.545 - 0.001); 

Floating point numbers are known to be problematic if you don’t know what is happening under the covers. In this case, it is best to write (or use) a decimal type library to alleviate problems.


Program Example:

 #include <stdio.h> int main (void) { printf("%.20f\n", 11.545); printf("%.2f\n", 11.545); printf("%.2f\n", 11.545 + 0.001); return 0; } 

outputs this in my Cygwin environment:

 11.54499999999999992895 11.54 11.55 

which is suitable for your specific case (this does not happen, but we hope we will apply in another direction: you need to check it), but you should check the entire possible input range, if you want to be sure that it will work in all cases.


Update:

Eugene, based on your comment:

It works for this particular case, but not as a general solution. For example, if the number I want to format is 0.545 instead of 11.545, then "% .2f"% (0.545 - 0.001) returns "0.54", and "% .2f"% 0.545 on Linux correctly returns "0.55".

so I said that you would need to check the whole range to see if it would work, and why I said that the decimal data type would be preferable.

If you need decimal precision, this is what you will need to do. But you can consider cases in this range where Linux also goes the other way (according to your comment) - there may be a situation where Linux and Windows do not agree in the opposite direction to what you found - the decimal type probably won Solve this.

You may need to make the comparison tools a little smarter, as they can ignore the difference of 1 in the final fractional place.

0
source share

You may be able to subtract the value several times from the value to round the rounding.

 print "%.2f"%(11.545-1e-12) 
0
source share

All Articles