0

If I have a double variable, I want to store its value in a file to two decimal places with the standard rules of rounding off. I don't just want to store and display data up to two decimal places but I also want to do arithmetic using this data, when I reopen the file, exactly up to two decimal places. In other words when I read this data from the file into a double variable, I want this data to be accurately loaded to two decimal places also, exaclty as I stored it. Using <iomanip> and fixed and setprecision from what I have seen so far are only used for displaying the output to be used with std::cout. I may be wrong though. Please explain using the following example how to accomplish this? Also how to store data from a double variable up to two decimal places in a binary file also?

std::ofstream file("data.txt");
int hours = 8, mins= 35;
char ap='p';

double format_24_hr=0.00;
format_24_hr = static_cast<double>(hours) + 12.00 + static_cast<double>(mins)/60.00;
file<<format_24_hr;  // how to store double to two decimal places in a binary file
file.close();

Also, when I'm reading the file like follows, how do I make sure that it's loading the data to two decimal places exactly as I stored it?

std::ifstream file("data.txt");
double var=0.00;
file>>var;   // would the data read here be exactly loaded to two decimal places as I stored it?
file.close();
  • 4
    What you're asking for is physically impossible. Binary based `double`s can't hold all decimal values exactly. – Mark Ransom Jan 12 '21 at 02:41
  • Your stated requirements are incompatible with what `double` is and how it works. You cannot use `double`, easily, in this manner, and it will be necessary to twist oneself into pretzels in order to attempt to make doubles work like that (speaking from experience). Your best avenue is to use one of several widely available, special-purpose, fixed precision libraries. – Sam Varshavchik Jan 12 '21 at 02:42
  • 1
    To be more concrete about it, you can store `4.25` exactly but the closest you can get to `4.26` is `4.2599999999999997868371792719699442386627197265625`. – Mark Ransom Jan 12 '21 at 02:45
  • 5
    Store/read your data as *integer* values of 100 times their 'raw' values. – Adrian Mole Jan 12 '21 at 02:45
  • 1
    Why do you have your value stored in a *floating point* representation when all of your requirements amount to [tag:fixed-point] representations? Are you allowed to change the type of your variable? – JaMiT Jan 12 '21 at 02:57
  • @JaMiT yes I'm allowed to change type of my variable. I'm basically trying to store total number of hours between two times and so I need to convert them to 24 hour format first. So, I'm using double. I don't know why exactly is double not suitable to be used in this manner to write to files! – Pratap Biswakarma Jan 12 '21 at 13:02
  • 1
    See [Is floating point math broken?](https://stackoverflow.com/q/588004/5987) and see if it makes things clearer. – Mark Ransom Jan 12 '21 at 23:00
  • @MarkRansom very helpful. Is there a way to just store the floating point to two decimal points though, caring less about how the rounding off happens and again read it to two decimal places regardless of how rounding off happens? – Pratap Biswakarma Jan 13 '21 at 00:47
  • No, there's no way to just "store" only two decimal points, because the storage is not decimal. If your question is more about ensuring the number is properly rounded on input and output, that makes it much broader and possibly too broad for a single question. – Mark Ransom Jan 13 '21 at 01:43

1 Answers1

1

What you have described is a fixed point representation. Trying to use a floating point representation for fixed point values often leads to issues, as you have discovered and as described in Is floating point math broken? See Fixed point vs Floating point number for a more detailed comparison of the representations.

A reasonable (and sometimes simple, depending on your needs) approach is to use an integer to represent your values. You could build an interface around this integer so that it is divided by 100 when viewed, making it look like a number with two digits after the decimal place. That is, if the stored value is 2058, code using that value would see (the nearest double value to) 20.58. Here is an example of a starting point:

class FixedPoint {
    int value;

public:
    double get() const { return static_cast<double>(value) / 100.0; }
    // Also define other needed functionality, including arithmetic.
    // Multiplication and division require a bit of care.
};

However your example code shows an attempt to store hours and minutes as fractional hours. This is already problematic because that association is not precise. For example, you cannot get 20.58 hours from an integral number of hours and minutes; you can get close (20.58333333...), but not exactly.

A more consistent approach than fractional hours would be total minutes. Instead of storing

static_cast<double>(hours) + 12.00 + static_cast<double>(mins)/60.00

to a file and dealing with precision issues, you could get perfect precision by storing

(hours + 12) * 60 + mins

This is an integer, side-stepping the question of how many decimal places to use. The exact same value will be restored when the saved value is read.

Save the fractional hours for human-readable output (if that is needed), and use the total number of minutes for values that need to be stored and retrieved by your program.

JaMiT
  • 14,422
  • 4
  • 15
  • 31