0

I have a python script within which I define a variable called dt and initialize it as such: dt=0.30. For some specific reason, I have had to convert my python script into a C++ program, but the two programs are written to produce the exact same results. The problem is that the results start to deviate at some point. I believe the problem occurs due to the fact that 0.03 does not have an exact representation in binary floating point in python; whereas in C++ dt=0.30 gives 0.300000000000, the python output to my file gives dt=0.300000011921.

What I really need is a way to force dt=0.30 precisely in python, so that I can then compare my python results with my C++ code, for I believe the difference between them is simply in this small difference, which over runs builds up to a substantial difference. I have therefore looked into the decimal arithmetic in python by calling from decimal import *, but I then cannot multiply dt by any floating point number (which I need to do for the calculations in the code). Does anyone know of a simple way of forcing dt to exactly 0.30 without using the 'decimal floating point' environment?

inquiries
  • 385
  • 2
  • 7
  • 20
  • 3
    It does not have an exact representation on any system that stores data as binary values ;) – Bob Dylan Jan 11 '16 at 17:13
  • 1
    Also see: [Is floating point math broken?](http://stackoverflow.com/questions/588004/is-floating-point-math-broken) – NathanOliver Jan 11 '16 at 17:14
  • @BobDylan What about systems / hardware that does not use IEEE 754? – simon Jan 11 '16 at 17:16
  • 2
    If your Python implementation's output says `dt=0.300000011921` when you initialized it as `dt=0.30`, then you're doing something weird and wrong. It should be accurate to way more digits than that. – user2357112 Jan 11 '16 at 17:17
  • @user2357112 I can assure you that my code has dt set to 0.30. So might it have something to do with writing to the file itself which changes the number a bit? – inquiries Jan 11 '16 at 17:19
  • @gurka it's still stored in binary and 'forced' into decimal representation somehow – Bob Dylan Jan 11 '16 at 17:21
  • 1
    Also, is the OP storing it as `float` in C++ rather than `double`? – Bob Dylan Jan 11 '16 at 17:22
  • In C++, my dt is accessible to the program through a definition: `#define dt 0.30` – inquiries Jan 11 '16 at 17:23
  • 1
    @user4437416: Your Python output looks like what you'd get for a single-precision floating-point number, which wouldn't be possible unless you did something like downloading a specialized library like NumPy and then explicitly telling it to use single-precision. It wouldn't happen for an initialization like `dt=0.30`. – user2357112 Jan 11 '16 at 17:26
  • So @user44374416, is there a way to change this? – inquiries Jan 11 '16 at 17:28
  • Why down vote this post? It is a genuine problem of a subtle nature which you yourself cannot begin to comprehend. – inquiries Jan 11 '16 at 18:12
  • 1
    You've just encountered a common problem with text <-> internal representation. Consider outputting the values as hex, using Python's `float.hex()` method and C's `printf` "%a", if your compiler's runtime supports it (and associated scanf methods, as applicable.) If you output values as hex, you'll notice that the floating point value's units-last-place (ULP) may differ, which generally isn't much of a problem (it's expected as the result of differences in text conversion libraries.) – scooter me fecit Jan 11 '16 at 20:06
  • I hate to vote to close. This problem comes up all the time, surprises everybody, and is a reasonable question. But because it comes up all the time, it's been answered already. More than once. – Max Lybbert Jan 11 '16 at 20:22
  • 1
    Since you didn't give any of the Python or C++ code, it's impossible for us to say why there's a divergence. – Mark Ransom Jan 11 '16 at 20:25
  • 1
    @MaxLybbert: At the risk of quibbling, it's possible that there are different manifestations of the same problem without a reasonably coherent explanation. It basically comes down the text conversion and ULP noise, at least in this question. The post you pointed to isn't quite the same problem, although it does deal with issues in floating point arithmetic and text conversion. – scooter me fecit Jan 11 '16 at 20:34
  • I was writing a test for some code that reads values over a serial connection to a sensor. To mock out the sensor, I created a Python script that exposes the same interface, but sends random data. I used `socat` to create a pair of 'virtual' serial ports, then had the Python program write to one port, and the program-under-test read from the other. Each program logged what it sent or received to a CSV file so I could confirm the two files were identical afterwards. I wound up using `ctypes.c_float` to store the values in Python so they would match the C program's output. – evadeflow Apr 10 '20 at 21:54

2 Answers2

3

If your C compiler's printf runtime supports hex floating point output, here's a test you can try to see the differences between text conversion functions:

Python:

Python 2.7.10 (default, Aug 24 2015, 14:04:43)
[GCC 4.2.1 Compatible Apple LLVM 6.1.0 (clang-602.0.53)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> a = float(0.3)
>>> a.hex()
'0x1.3333333333333p-2'

C:

#include <stdio.h>

int main(void)
{
  float x = 0.3;

  printf("%a\n", x);
  return 0;
}

Output from the compiled C program:

0x1.333334p-2

Note that the units-last-place (ULP) in the output differ by 1 and the float produced by Python looks like an IEEE double.

It's normal to have minor ULP differences. It just shows you that there are differences in how runtime libraries convert floating point numbers and deal with rounding.

Alternatively, you could print hex floats in Python and use them as constants in your C source:

#include <stdio.h>

int main(void)
{
  float y = 0x1.333334p-2;

  printf("%f\n", y);
  return 0;
}

Or:

#include <stdio.h>

int main(void)
{
  float y = 0x1.333334p-2;
  double z = 0x1.3333333333333p-2;

  printf("y = %f\n", y);
  printf("z = %f\n", z);

  return 0;
}

Output:

y = 0.300000
z = 0.300000
scooter me fecit
  • 1,053
  • 5
  • 15
2

I do not have your context, but try this:

$ python
> from decimal import Decimal
> dt = Decimal('0.30')
> sum = Decimal(0)
> for _ in range(1000000000): sum += dt    
> sum
Decimal('30000000.00')

Note that Decimal is called with a string.

Check for instance the difference:

> Decimal(0.30)
Decimal('0.299999999999999988897769753748434595763683319091796875')
> Decimal('0.30')
Decimal('0.30')
arve0
  • 3,424
  • 26
  • 33
  • Converting everything to `Decimal` will make the Python figures more accurate, but they still won't match the C++ ones. – Mark Ransom Jan 11 '16 at 20:24
  • Good catch, "I believe the problem occurs due to the fact that 0.03 does not have an exact representation in binary floating point in python" wrongly leaded me into making the python version more accurate. – arve0 Jan 11 '16 at 22:00