-1

It would seem fmod(x,1) where x is a double gives the wrong result, as output by the line:

std::cout << fmod(min, 1) << "|" << fmod(max, 1) << std::endl;

I forgot the name for what you call this, but this is the smallest amount of code necessary to illustrate my problem:

#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <time.h>
#include <math.h>
const int deviation = 3;
void weightedRandomNumber(double min, double max);
int main() {
    srand(time(nullptr));
    std::cout.precision(16);
    std::cout << 123.1 << "|" << 2789.3234 << std::endl;
    weightedRandomNumber(123.1, 2789.3234);
    system("pause");
    return 0;
}
void weightedRandomNumber(double min, double max) {//inclusive
    int multiplier = 1;
    std::cout << min << "|" << max << std::endl;
    while (fmod(min, 1) > 0 || fmod(max, 1) > 0) {
        std::cout << min << "|" << max << std::endl;
        std::cout << fmod(min, 1) << "|" << fmod(max, 1) << std::endl;
        min *= 10;
        max *= 10;
        multiplier++;
    }
    std::cout << min << "|" << max << std::endl;
    std::cout << multiplier << std::endl;
}

The outputs I get when I run the code are as such:

123.1|2789.3234
123.1|2789.3234
123.1|2789.3234
0.09999999999999432|0.3234000000002197
1231|27893.234
0|0.2340000000040163
12310|278932.34
0|0.3400000000256114
123100|2789323.4
0|0.400000000372529
1231000|27893234
0|3.725290298461914e-09
12310000|278932340.0000001
0|5.960464477539063e-08
123100000|2789323400
0|4.76837158203125e-07
1231000000|27893234000
0|3.814697265625e-06
12310000000|278932340000.0001
0|6.103515625e-05
123100000000|2789323400000
0|0.00048828125
1231000000000|27893234000000
0|0.00390625
12310000000000|278932340000000
0|0.03125
123100000000000|2789323400000001
0|0.5
1231000000000000|2.7893234e+16
14

Other than this I don't quite know what to say, if I have missed anything necessary please comment so I can amend my question.

Jonathan Woollett-light
  • 2,813
  • 5
  • 30
  • 58

1 Answers1

1

The issue is not with fmod, which is giving the highest precision results it can. The issue is with cout precision not behaving like you expect, combined with "rounding" because a double cannot store 0.1 accurately enough to represent what cout considers a precision of 16.

This code demonstrates the issue. The rounding actually occurs when you assign 123.1 to a double, but because of the 3 digits to the left is not visible until it becomes a smaller number.

int main() {
      std::cout.precision(16);
      std::cout << (123.1L - 123L);
    }

output:

0.09999999999999432

Actually....this illustrates the problem even more succinctly:

int main() {
  std::cout.precision(20);
  std::cout << 123.1;
}

123.09999999999999432

Further reading from the comments on your question: Is floating point math broken?

Also, for the vast majority of scenarios, a double is more than fine. For accurate, recursive math, you'd want to consider a heavy-duty math library, or even a math-specialized language.

Further further reading: http://www.boost.org/doc/libs/1_62_0/libs/math/doc/html/math_toolkit/high_precision/why_high_precision.html

zzxyz
  • 2,953
  • 1
  • 16
  • 31
  • Okay, but why does 123.1 convert to 123.09999999999999432 rather than 123.10000000000000000? – Jonathan Woollett-light Jan 10 '18 at 01:37
  • Answer updated. As far as your first question, generally, you want to pick a precision the double will always be capable of storing accurately. More generally, you just want to leave `cout` alone and let it do it's job. (don't use `precision` unless you know how much precision you can accurately represent) – zzxyz Jan 10 '18 at 01:39
  • Perhaps this is an element of the problem, but even after removing 'std::cout.precision()' it is still broken, the output is merely changed. As shown here: https://pastebin.com/QXQcJsi9 – Jonathan Woollett-light Jan 10 '18 at 01:52
  • @JonathanWoollett-light Yes, this might be one of the rare cases where a double doesn't have enough precision to do what you're asking of it--I didn't really check the math continuing further on. *Normally* the problem is more trivial, like an output issue or comparing .99999 to .99998 and trying to decide whether they are effectively equal for your purposes. – zzxyz Jan 10 '18 at 01:56