Почему std :: round (sin (pi / 6)) не равно 1? - PullRequest
3 голосов
/ 19 июня 2020

В документации cppreference указано, что std :: round будет специально округлять от нуля в «промежуточных случаях». Хотя это верно для литерала 0.5, это не так для std::sin(pi/6). Я подумал, что это ошибка с плавающей запятой, поэтому напечатал значение, но оно точно 0.5. Однако после проверки двоичного представления я вижу, что они действительно представлены по-другому. Я предоставил код, который использовал для этих проверок, ниже.

#include <iostream>
#include <stdio.h>
#include <cmath>

int main(int argc, char * argv[])
{
    double const pi = std::acos(-1);
    double const a = std::sin(pi/6);
    double const b = 0.5;

    std::cout << "round(" << a << ") = " << std::round(a) << "\n";

    auto pa = reinterpret_cast<const unsigned char *>(&a);
    auto pb = reinterpret_cast<const unsigned char *>(&b);

    std::cout << "a = 0x";
    for (size_t i = 0; i != sizeof(double); ++i) {
        printf("%02x", pa[i]);
    }
    std::cout << "\nb = 0x";
    for (size_t i = 0; i != sizeof(double); ++i) {
        printf("%02x", pb[i]);
    }
    std::cout << "\n";
}
round(0.5) = 0
round(0.5) = 1
a = 0xffffffffffffdf3f
b = 0x000000000000e03f

Итак, мой вопрос, является ли это поведение округления частью спецификации C ++ или это ошибка? И в любом случае, есть ли какой-нибудь общий способ «исправить» представление значения, возвращаемого sin? Я не уверен, в каком он формате, потому что, исходя из того, что я знаю о IEEE-754, похоже, что это должно быть NaN. Хотя, насколько я понимаю, c ++ не гарантирует представления с плавающей запятой IEEE-754?

Ответы [ 2 ]

2 голосов
/ 19 июня 2020

Проблема в том, что вы не печатаете значение с достаточным количеством значащих цифр. Когда я увеличиваю точность с помощью std::setprecision(20), я получаю: round(0.49999999999999994449) = 0.

Вы можете убедиться в этом сами, изменив код или введя 3fdfffffffffffff в нижнее шестнадцатеричное поле этого онлайн-калькулятора: https://baseconvert.com/ieee-754-floating-point

1 голос
/ 19 июня 2020

Представление выглядит как NaN, потому что вы читаете его задом наперед. x86 / x64 имеют числа с плавающей запятой с прямым порядком байтов. Таким образом, вы должны прочитать его от старшего к младшему адресу, что дает 0x3fdfffff..., что, конечно, немного меньше 0,5.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...