0

I am trying to round in scientific notation but considering the correct rule which is: A) for all decimal numbers finishing by 0, 1, 2, 3, 4 the rounding must be at the low order of magnitude B) for all decimal numbers finishing by 5, 6, 7, 8, 9 the rounding must be up.

I did the following function:

std::string MyClass::doubleToString(double num, int precision) {
    std::ostringstream ss;
    ss << std::scientific << std::setprecision(precision) << num;
    return ss.str();
}

For instance, when I call doubleToString(0.35, 0), it returns 3E-1 while it should return 4E-1. I think it is a very simple question but I cannot figure out why I do not get the correct behavior.

froz
  • 163
  • 1
  • 12
  • 4
    [Is floating point math broken?](https://stackoverflow.com/questions/588004/is-floating-point-math-broken) – n. m. could be an AI Jan 22 '23 at 19:51
  • Ok I understand this but how can I have the expected behavior in my code ? – froz Jan 22 '23 at 19:57
  • Have you read through the answer in the provided question link? If you do understand then how come you are asking such a question? You can't, this is how computers work. If you wish to minimize the floating point error there is a branch of mathematics called "numerical mathematics" that focus exactly on this topic... – Milan Š. Jan 22 '23 at 19:59
  • To see the true value of a `double` you should print it to at least 53 significant digits. – Mark Ransom Jan 22 '23 at 20:21
  • 2
    This should make it clear - live - https://godbolt.org/z/sfE8PshMP `3.5` is stored as `3.4999999999999997780e-01` (approx). – Richard Critten Jan 22 '23 at 20:23
  • @RichardCritten very nice visual explanation of what is happening, IMHO it should be remade into an answer. – Milan Š. Jan 22 '23 at 20:26
  • Thank you guys. I have tried the following solution: ss << std::scientific << std::setprecision(precision) << num + 1e-14; It seems adding the 1e-14 solves the problem. I hope there is no exception in which it creates an issue – froz Jan 22 '23 at 20:27
  • @froz - So you have solved the issue for `0.35`? What if there is another value, like `1.5e200`? – BoP Jan 22 '23 at 20:32
  • @froz I don't think you understand how floating point arithmetics work. What you are doing might work for one number, but not for a different one, or on a different architecture. EDIT: [Floating point arithmetics on wikipedia](https://en.wikipedia.org/wiki/Floating-point_arithmetic) even if you would just read this. It would give you an idea as to why what you are doing is just **bad**. – Milan Š. Jan 22 '23 at 20:32
  • My values will be between 1e-6 and 1e+3. Hence considering the double precision that is around 17 decimals, it should work by adding 1e-14. Don't you think? The issue seems not to exist when rounding 0.35 using python. – froz Jan 22 '23 at 20:36
  • 1
    If 0.35 requires 1e-14 then 3500 will require 3500/0.35 * 1e-14 added. – QuentinUK Jan 22 '23 at 23:31
  • 1
    The only problem now is that rounding 3.4999999999999997780e-01 should give 3.4 but gives 3.5. – QuentinUK Jan 22 '23 at 23:47
  • 1
    "it should work by adding 1e-14." It won't – n. m. could be an AI Jan 23 '23 at 06:03
  • what about adding the 'value * 1e-14' ? I understand guys that it is a tricky thing and that the issue is linked to the way numbers are stored. But it certainly exists solutions. I mean C++ is used in a lot of applications. I cannot believe that the NASA for instance, is having such precision problems ! So please, I have come here to ask for your expertise and knowledge, to help me find out a way. Saying that something won't work or that I don't understand the arithmetics are just half helping and I am grateful you showed me the issue (especially the example of @RichardCritten and @Bob_) – froz Jan 23 '23 at 07:20
  • This is not a "tricky" thing. Floating point numbers are not decimal fractions. It makes no sense to try and apply decimal rounding rules to them. If you try, you will sooner or later find out that you are wasting your time. I am writing this so that it happens sooner than later. But if you insist on finding out the painful way, I cannot stop you. Go ahead and try. – n. m. could be an AI Jan 23 '23 at 11:43
  • @froz NASA has these precision problems, they know about them and they work around them, that's why I pointed you towards **numerical mathematics**, mathematicians from this branch of mathematics were the first to get images out of cameras used in space. I'm in all honesty starting to feel that you are trolling us here. Your only option here is to design your own data structure. But if that's the case then forget about storing numbers in 32/64bits. And get ready to design your own arithmetic operations. – Milan Š. Jan 23 '23 at 15:35

1 Answers1

0

It seems that you are trying to round half up1.

In C++ you can try to change the floating-point rounding mode using std::fesetround:

#include <iostream>
#include <iomanip>
#include <cfenv>

int main()
{
  std::cout << std::setprecision(0) << std::scientific;

  std::fesetround(FE_DOWNWARD);
  std::cout << "rounding down: " << 0.35 << "\n\n";         // --> 3e-01

  std::fesetround(FE_UPWARD);
  std::cout << "rounding up: " << 0.35 << '\n';             // --> 4e-01
}

See e.g. https://godbolt.org/z/WhGe89one

Note though, that this won't produce the output you want.


  1. https://en.wikipedia.org/wiki/Rounding#Rounding_half_up
Bob__
  • 12,361
  • 3
  • 28
  • 42