@hauptling

Почему меняется выходное значение после нескольких запусков программы?

#include <iostream>
#include <math.h>

int main()
{
    double var = 10.288;
    uint32_t number;

    
    var -= (uint32_t)var;

    
    while(var - floor(var) > 0.0)
    {
        var *= 10;
    }
    
    
    number = (uint32_t)var;
    
    std::cout << number;
    
}


Задача: вытащить дробную часть из double и привести ее к uint32_t;
после несколько запусков бывает выводит мусор, с чем может быть связано?
  • Вопрос задан
  • 183 просмотра
Решения вопроса 1
@Mercury13
Программист на «си с крестами» и не только
Компилятор какой используете? У меня на MinGW всё постоянно — и постоянно не дробная часть. Есть две возможные причины нестабильного поведения.
1. Флаги сопроцессора — какая-то библиотека Си их не выставляет в постоянное значение.
2. Неопределённое поведение (uint32_t)var в предпоследней строке. Число-то >1016, а uint32 — это 4·109.

Тут вы множите число на 10, пока оно не станет целым. Если оно не точный double (а оно не точный), получается ситуёвина, когда младшие биты зависят от флагов сопроцессора, и неизвестно, сколько итераций проработает программа. В любом случае крайне мала вероятность, что будет нечто умещающееся в uint32_t. Таким образом, есть такие варианты обойти проблему.
1) Умножить сразу на 1e8, а затем обрезать нули.
2) Сконвертировать в uint64_t.

Преобразование в uint64_t даёт постоянные 28800000000000024.
Умножение на 1e8 (число подобрано такое, чтобы в uint32_t вмещалось)
var *= 1e8;
    number = (uint64_t)var;

    while (number % 10 == 0)
        number /= 10;

даёт 288.

UPD. Сам я для преобразования дробного в строку в коммерческой проге использую собственную функцию, основанную на GRISU. Там есть много функций наподобие: точность 7 знаков, но если число <1010 — выводим его как целое, с 9-ю знаками.
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 1
Adamos
@Adamos
Задача "представить то, что в десятичной дроби находится после запятой, в виде целого числа" - вообще не математическая. Потому что дает чрезвычайно разные результаты на 1.1 и 1.1000000000001.
Поэтому решать ее проще так же дебильно: например, вывести дробь в строку, найти в ней с одной стороны точку, а с другой - первую ненулевую цифру, а потом эту часть строки привести к целому.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы