Почему это так?

Это стандартный вопрос IEEE 754. Я не совсем понимаю механику, стоящую за ней.

public class Gray {  
    public static void main(String[] args){
        System.out.println( (float) (2000000000) == (float) (2000000000 + 50));
    }
}

Ответ 1

Поскольку float может содержать только от 7 до 8 значащих цифр. То есть, у него недостаточно битов, чтобы точно представлять число 2000000050, поэтому оно округляется до 2000000000.

В частности, a float состоит из трех частей:

  • знаковый бит (1 бит)
  • показатель экспоненты (8 бит)
  • значение (24 бита, но только 23 бита сохраняются, так как MSB значимости всегда 1)

Вы можете думать о плавающей запятой как о том, как компьютер делает научную нотацию, но в двоичном формате.

Точность равна log(2 ^ number of significand bits). Это означает, что float может содержать значимые цифры log(2 ^ 24) = 7.225.

Число 2 000 000 050 имеет 9 значащих цифр. Вышеприведенный расчет говорит нам, что 24-битное значение не может содержать много значительных цифр. Причина, по которой работает 2 000 000 000, потому что есть только 1 значащая цифра, поэтому она соответствует значению.

Чтобы решить проблему, вы должны использовать double, поскольку она имеет 52-битное значение, что более чем достаточно для представления всевозможные 32-разрядные номера.

Ответ 2

Положительно сказано: 50 - ошибка округления, когда поплавок имеет значение в два миллиарда.

Ответ 3

Вы можете найти этот трюк, чтобы найти следующее представляемое значение.

float f = 2000000000;
int binaryValue = Float.floatToRawIntBits(f);
int nextBinaryValue = binaryValue+1;
float nextFloat = Float.intBitsToFloat(nextBinaryValue);
System.out.printf("The next float value after %.0f is %.0f%n",  f, nextFloat);

double d = 2000000000;
long binaryValue2 = Double.doubleToRawLongBits(d);
long nextBinaryValue2 = binaryValue2+1;
double nextDouble = Double.longBitsToDouble(nextBinaryValue2);
System.out.printf("The next double value after %.7f is %.7f%n",  d, nextDouble);

печатает

The next float value after 2000000000 is 2000000128
The next double value after 2000000000.0000000 is 2000000000.0000002

Ответ 4

Это может помочь вам понять ситуацию, если вы рассматриваете программу (С++), как показано ниже. Он отображает группы последовательных целых чисел, которые округляются до одного и того же значения float:

#include <iostream>                                                             
#include <iomanip>                                                              

int main()                                                                      
{                                                                               
    float prev = 0;                                                             
    int count = 0;                                                              
    double from;                                                                
    for (double to = 2000000000 - 150; count < 10; to += 1.0)                   
    {                                                                           
        float now = to;                                                         
        if (now != prev)                                                        
        {                                                                       
            if (count)                                                          
                std::cout << std::setprecision(20) << from << ".." << to - 1 << " ==> " << prev << '\n';                                                        
            prev = now;                                                         
            from = to;                                                          
            ++count;                                                            
        }                                                                       
    }                                                                           
}

Вывод:

1999999850..1999999935 ==> 1999999872
1999999936..2000000064 ==> 2000000000
2000000065..2000000191 ==> 2000000128
2000000192..2000000320 ==> 2000000256
2000000321..2000000447 ==> 2000000384
2000000448..2000000576 ==> 2000000512
2000000577..2000000703 ==> 2000000640
2000000704..2000000832 ==> 2000000768
2000000833..2000000959 ==> 2000000896

Это указывает на то, что плавающая точка достаточно точная, чтобы представлять все целые числа от 1999999850 до 1999999935, ошибочно записывая их значение как 1999999872. Итак, для других значений. Это ощутимое следствие ограниченного пространства для хранения, упомянутого выше.