7

I have a situation when the classic representation error in Python started to be a problem: I need them to be used for Matrix operations in Numpy and the decimal type is not supported yet.

You all know that if I do 111.85 * 111.85 I will get 12510.422499999999 but if I round(12510.422499999999, 4) I can get the proper result which is of course 12510.4225.

But the actual questions are:

  • Is this round stuff a good idea and a good practice?
  • Will this work for all cases? sometimes the decimals position where the ..999 decimals could be more
  • And finally, How to get the appropriate number of decimal positions to use with the round for all possible values?
gerosalesc
  • 2,983
  • 3
  • 27
  • 46
  • If you want more accuracy than floats can provide, consider using the `decimal` or `fraction` modules. – Kevin Jan 06 '16 at 19:44
  • 7
    No, you *don't* get the "proper" result from the `round` operation! It might *look* like `12510.4225` when printed, but the value that Python's storing is actually `12510.422500000000582076609134674072265625`. Now you need to ask yourself whether that's really any better for your purposes than the original result of `12510.422499999998763087205588817596435546875`. (And the answer is 'probably not'.) – Mark Dickinson Jan 06 '16 at 19:45
  • @Kevin I need those values to be supported by *Numpy* for some matrix operations, and I think decimal/fraction are still not supported – gerosalesc Jan 06 '16 at 19:46
  • (And for more ranting along the lines above, see http://stackoverflow.com/a/22155830/270986.) – Mark Dickinson Jan 06 '16 at 19:46
  • 1
    A possible solution is to move the `.` to the right until you have an integer (in your example, you will move 2 positions to get `11185`) and then you can multiply integers, and will manually add a `.` in the correct place (which will be 2+2=4 positions to the left) – William Jan 06 '16 at 19:47
  • Sometimes using `decimal` module may be a much better idea. Check out [this](https://docs.python.org/3/library/decimal.html) to decide if it fits your goal. – vrs Jan 06 '16 at 19:47
  • @wil93 Generally, if you're doing calculations with floating point numbers (like with matrices), they're not really interchangeable with integers because many math operations (eg. division, square root) will require floats to represent the result. Moving the decimal doesn't really change the inaccuracy, it just changes which parts of the float are more significant. – Brendan Abel Jan 06 '16 at 21:15

2 Answers2

6

Even if you round the number, the decimal still won't be exactly what you expect it to be. Python may display only the most significant bits, but the underlying decimal inaccuracy is still there.

The python docs devote a section to it

Many users are not aware of the approximation because of the way values are displayed. Python only prints a decimal approximation to the true decimal value of the binary approximation stored by the machine. On most machines, if Python were to print the true decimal value of the binary approximation stored for 0.1, it would have to display

>>> 0.1
0.1000000000000000055511151231257827021181583404541015625

That is more digits than most people find useful, so Python keeps the number of digits manageable by displaying a rounded value instead

>>> 1 / 10
0.1

For most use cases, the inaccuracy can safely be ignored. If you are dealing with extremely small or extremely large numbers or need accuracy out to many decimal places, you can use the decimal library

 >>> Decimal('111.85') * Decimal('111.85')
 Decimal('12510.4225')
Brendan Abel
  • 35,343
  • 14
  • 88
  • 118
5

The real issue here is to identify how the representation error could start to be a problem in the first place.

You should not be using the representation for human-readable printing of floats, you should be using for example str.format or some other presentation method.

Keep your numbers as numbers for all calculations (whether you do that with floats or Decimals or whatever...) and only round or truncate things at presentation.

wim
  • 338,267
  • 99
  • 616
  • 750
  • Is not actually a displaying human-readable values, I use those values for calculations but for this case I would like more accuracy, and i would like to use Decimals but Numpy doesn't support this type as long as i know. – gerosalesc Jan 06 '16 at 19:54
  • 2
    @GermanRosales: Your original pre-rounded answer is already accurate to less than 0.7 ulps. If that level of accuracy isn't good enough (why not?) then float64 isn't going to meet your needs. But you should figure out whether you really do need that level of accuracy. – Mark Dickinson Jan 06 '16 at 20:00
  • @MarkDickinson I am afraid float64 doesn't fit my needs, but I can't do matrix inverse operation with float128 (I am getting an error with numpy's inverse operation) – gerosalesc Jan 06 '16 at 20:05
  • 1
    @GermanRosales: Hmm, then it sounds like you've got a hard problem to solve. At any rate, `round` isn't going to help you: I think there are already a couple of good answers to your posted question here. Maybe you should ask a new question about high-precision matrix inversion? (Or maybe there are numerical reformulations of your problem that would avoid the need for the high precision in the first place.) – Mark Dickinson Jan 06 '16 at 20:16
  • 1
    In my experience, almost all of the time when guys think they need higher precision arithmetic they actually don't. They just need smarter maths – wim Jan 06 '16 at 20:17
  • @MarkDickinson Yep I will open a new question regarding the problem with the inverse matrix function and the float128 type and a possible solution for matrix inversion outside numpy, thanks – gerosalesc Jan 06 '16 at 20:20
  • @wim I am not the math guy in this project, but you may still be right – gerosalesc Jan 06 '16 at 20:22
  • @GermanRosales *"I am afraid float64 doesn't fit my needs, but I can't do matrix inverse operation with float128 (I am getting an error with numpy's inverse operation)"* - this is the question you should be asking – ali_m Jan 06 '16 at 22:21
  • @ali_m Yep i figured out thanks to comments and answers, so I will be asking again – gerosalesc Jan 06 '16 at 22:57