3

I read here and concluded that the minimum(absolute ...) value for double is 1.7e-308 but my code loops forever:

for (double d(-1.0); d <= 1.0; d+=1.7e-308)
{
}

Edit: I want to loop from -1.0 to 1.0 in the smallest possible increment.

Peter G.
  • 14,786
  • 7
  • 57
  • 75
  • 2
    Even if the code was correct, it would take a long time to loop through every value. 64 bits is a lot even when you subtract out the NaNs. – Mysticial Apr 20 '14 at 08:30

3 Answers3

10

In C++11/C99 you can use nextafter to get the next representable number.

#include <cmath>

for (double d(-1.0); d <= 1.0; d = std::nextafter(d, 1.0) )
{
}

Also if I did the math correctly there are approximately 10^18 representable values between -1 and 1.

With the reasonable assumption of 10^6 cycles per second it will take 32000 years to perform this computation.

sbabbi
  • 11,070
  • 2
  • 29
  • 57
  • I estimated the count for (-1,1) as greater than 9e18. There are two sign bit values. For each sign value there are 1023 exponents (0 through 0x3fe). For each exponent, there are 2^52 significands. Java evaluates `2*1023*Math.pow(2, 52)` to `9.2143648376000348E18`. – Patricia Shanahan Apr 20 '14 at 09:24
  • @PatriciaShanahan You are right, I'll fix the answer – sbabbi Apr 20 '14 at 09:28
3

TL;DR: the gap between the numbers that a computer is able to store in floating point numbers gets larger as the number gets larger. Your minimum number is only valid when the number is zero. At some point, trying to add such a small number does nothing as it can't represent such a small change in number.

This is because the gap between each representable floating point numbers is larger as your number increases in magnitude.

The difference between 1 and the closest representable smaller than 1 is about 1.11 × 10-16, much larger than the value that you're trying to add.

What happens is that a double is stored with one set of bits for the sign of the number, another to represent the number itself (as an unsigned integer) and a multiplier as an exponent of 2.

When you are near zero, the exponent is very small, making one change in the actual number stored less than if your exponent is much larger.

For example, (4 - 3) × 299 is obviously going to be larger than (4 - 3) × 2-99, which is what is basically happening.

When you start adding, you'll find that eventually, adding 1.7 × 10-308 does nothing as your current value is the closest representable value for the addition, which keeps going on and on in an infinite loop.

Note that this is only when the rounding mode is set to roundTiesToEven or roundTiesToAway, where they round to the nearest representable value, except when they are equally near.

If you have manually set the rounding mode to roundTowardPositive by calling std::fesetround with FE_UPWARD beforehand (as Marc Glisse noted), if you add any positive number (except zero), it would keep increasing. I think roundTowardNegative and roundTowardZero are self-explanatory.

BTW, even if you replace that with 1.11 × 10-16, you're still going to have to go through 18 014 398 509 481 984 (18 quadrillion) iterations before you finish. If you actually loop through each of the values between -1 and 1, that's 9 223 372 036 854 775 809 (9 quintillion) iterations. Good luck.

Community
  • 1
  • 1
Qantas 94 Heavy
  • 15,750
  • 31
  • 68
  • 83
  • Also, even if you did write a correct loop over the set of possible values of a `double`, it would take a while to execute because there are roughly 2^64 of them. We're talking something in excess of a hundred years on a single CPU. – user3553031 Apr 20 '14 at 08:32
  • okay I get it now, you can represent really really small numbers with a floating point and exponents, what I actually want is something like 0.000000000000001 – user3345413 Apr 20 '14 at 08:37
  • 1
    You can change the rounding mode (with `fesetround`) towards +infinity so that adding any positive number will always increase the value. – Marc Glisse Apr 20 '14 at 10:05
  • 1
    At step size 0.000000000000001 getting from -1 to 1 will take 2e15 iterations. At 10 ns per iteration, that is 20,000,000 seconds, over 5555 hours or 231 days. What is the real objective? Is there some basis for picking a step size other than "the smallest possible increment"? – Patricia Shanahan Apr 20 '14 at 14:11
0

I don't think the minimum value for double is 1.7e-308 it's probably more like -1.7 e308, 1.7e-308 is a really small number, but not a close number to negative infinity. It is a very small number close to 0.